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.
1537 lines
49 KiB
1537 lines
49 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Globalization; |
|
using System.IO; |
|
using System.Linq; |
|
|
|
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 = new string[] |
|
{ |
|
"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 GeneratedIdentifier(string id) |
|
{ |
|
return "__" + id; |
|
} |
|
|
|
public static string SafeIdentifier(string id) |
|
{ |
|
id = new string(((IEnumerable<char>)id) |
|
.Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray()); |
|
return Keywords.Contains(id) ? "@" + id : id; |
|
} |
|
|
|
public static string ToCSharpCallConv(CallingConvention convention) |
|
{ |
|
switch (convention) |
|
{ |
|
case CallingConvention.Default: |
|
return "Winapi"; |
|
case CallingConvention.C: |
|
return "Cdecl"; |
|
case CallingConvention.StdCall: |
|
return "StdCall"; |
|
case CallingConvention.ThisCall: |
|
return "ThisCall"; |
|
case CallingConvention.FastCall: |
|
return "FastCall"; |
|
} |
|
|
|
return "Winapi"; |
|
} |
|
} |
|
|
|
public class CSharpTextTemplate : TextTemplate |
|
{ |
|
public CSharpTypePrinter TypePrinter { get; set; } |
|
|
|
public override string FileExtension |
|
{ |
|
get { return "cs"; } |
|
} |
|
|
|
public CSharpTextTemplate(Driver driver, TranslationUnit unit) |
|
: base(driver, unit) |
|
{ |
|
TypePrinter = new CSharpTypePrinter(driver.TypeDatabase, driver.Library); |
|
} |
|
|
|
#region Identifiers |
|
|
|
public string QualifiedIdentifier(Declaration decl) |
|
{ |
|
if (Options.GenerateLibraryNamespace) |
|
return string.Format("{0}::{1}", Options.OutputNamespace, decl.QualifiedName); |
|
return string.Format("{0}", decl.QualifiedName); |
|
} |
|
|
|
public static string GeneratedIdentifier(string id) |
|
{ |
|
return Helpers.GeneratedIdentifier(id); |
|
} |
|
|
|
public static string SafeIdentifier(string id) |
|
{ |
|
return Helpers.SafeIdentifier(id); |
|
} |
|
|
|
#endregion |
|
|
|
public override void Generate() |
|
{ |
|
GenerateHeader(); |
|
|
|
WriteLine("using System;"); |
|
WriteLine("using System.Runtime.InteropServices;"); |
|
WriteLine("using System.Security;"); |
|
NewLine(); |
|
|
|
if (Options.GenerateLibraryNamespace) |
|
{ |
|
WriteLine("namespace {0}", SafeIdentifier(Driver.Options.OutputNamespace)); |
|
WriteStartBraceIndent(); |
|
} |
|
|
|
GenerateNamespace(TranslationUnit); |
|
|
|
if (Options.GenerateLibraryNamespace) |
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
public void GenerateHeader() |
|
{ |
|
if (Transform != null) |
|
{ |
|
Transform.GenerateStart(this); |
|
return; |
|
} |
|
|
|
WriteLine("//----------------------------------------------------------------------------"); |
|
WriteLine("// This is autogenerated code by CppSharp."); |
|
WriteLine("// Do not edit this file or all your changes will be lost after re-generation."); |
|
WriteLine("//----------------------------------------------------------------------------"); |
|
} |
|
|
|
private void GenerateNamespace(Namespace @namespace) |
|
{ |
|
bool isGlobalNamespace = @namespace is TranslationUnit; |
|
|
|
if (!isGlobalNamespace) |
|
{ |
|
WriteLine("namespace {0}", @namespace.Name); |
|
WriteStartBraceIndent(); |
|
} |
|
|
|
// Generate all the enum declarations for the module. |
|
foreach (var @enum in @namespace.Enums) |
|
{ |
|
if (@enum.Ignore || @enum.IsIncomplete) |
|
continue; |
|
|
|
NewLineIfNeeded(); |
|
GenerateEnum(@enum); |
|
NeedNewLine(); |
|
} |
|
|
|
// Generate all the typedef declarations for the module. |
|
foreach (var typedef in @namespace.Typedefs) |
|
{ |
|
if (typedef.Ignore) continue; |
|
|
|
NewLineIfNeeded(); |
|
|
|
if (!GenerateTypedef(typedef)) |
|
continue; |
|
|
|
NeedNewLine(); |
|
} |
|
|
|
// Generate all the struct/class declarations for the module. |
|
foreach (var @class in @namespace.Classes) |
|
{ |
|
if (@class.Ignore || @class.IsIncomplete) |
|
continue; |
|
|
|
if (@class.IsDependent) |
|
continue; |
|
|
|
NewLineIfNeeded(); |
|
GenerateClass(@class); |
|
NeedNewLine(); |
|
} |
|
|
|
if (@namespace.HasFunctions) |
|
{ |
|
NewLineIfNeeded(); |
|
WriteLine("public partial class {0}{1}", SafeIdentifier(Options.LibraryName), |
|
TranslationUnit.FileNameWithoutExtension); |
|
WriteStartBraceIndent(); |
|
|
|
// Generate all the function declarations for the module. |
|
foreach (var function in @namespace.Functions) |
|
GenerateFunction(function); |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
foreach(var childNamespace in @namespace.Namespaces) |
|
GenerateNamespace(childNamespace); |
|
|
|
if (!isGlobalNamespace) |
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
public void GenerateDeclarationCommon(Declaration decl) |
|
{ |
|
GenerateSummary(decl.BriefComment); |
|
GenerateDebug(decl); |
|
} |
|
|
|
public void GenerateDebug(Declaration decl) |
|
{ |
|
if (Options.OutputDebug && !String.IsNullOrWhiteSpace(decl.DebugText)) |
|
WriteLine("// DEBUG: " + decl.DebugText); |
|
} |
|
|
|
public void GenerateSummary(string comment) |
|
{ |
|
if (String.IsNullOrWhiteSpace(comment)) |
|
return; |
|
|
|
WriteLine("/// <summary>"); |
|
WriteLine("/// {0}", comment); |
|
WriteLine("/// </summary>"); |
|
} |
|
|
|
public void GenerateInlineSummary(string comment) |
|
{ |
|
if (String.IsNullOrWhiteSpace(comment)) |
|
return; |
|
|
|
WriteLine("/// <summary>{0}</summary>", comment); |
|
} |
|
|
|
#region Classes |
|
|
|
public void GenerateClass(Class @class) |
|
{ |
|
if (@class.Ignore || @class.IsIncomplete) |
|
return; |
|
|
|
GenerateDeclarationCommon(@class); |
|
|
|
if (@class.IsUnion) |
|
{ |
|
// TODO: How to do wrapping of unions? |
|
throw new NotImplementedException(); |
|
} |
|
|
|
GenerateClassProlog(@class); |
|
|
|
NewLine(); |
|
WriteStartBraceIndent(); |
|
|
|
if (!@class.IsOpaque) |
|
{ |
|
GenerateClassInternals(@class); |
|
|
|
if (ShouldGenerateClassNativeField(@class)) |
|
{ |
|
NewLineIfNeeded(); |
|
WriteLine("public System.IntPtr Instance { get; protected set; }"); |
|
NeedNewLine(); |
|
} |
|
|
|
GenerateClassConstructors(@class); |
|
GenerateClassFields(@class); |
|
GenerateClassMethods(@class); |
|
GenerateClassVariables(@class); |
|
GenerateClassProperties(@class); |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
public void GenerateClassInternals(Class @class) |
|
{ |
|
var typePrinter = TypePrinter as CSharpTypePrinter; |
|
typePrinter.PushContext(CSharpTypePrinterContextKind.Native); |
|
|
|
WriteLine("[StructLayout(LayoutKind.Explicit, Size = {0})]", |
|
@class.Layout.Size); |
|
|
|
Write("internal "); |
|
|
|
if (@class.HasBaseClass) |
|
Write("new "); |
|
|
|
WriteLine("struct Internal"); |
|
WriteStartBraceIndent(); |
|
|
|
ResetNewLine(); |
|
|
|
foreach (var field in @class.Fields) |
|
{ |
|
NewLineIfNeeded(); |
|
|
|
if (field.Ignore) |
|
continue; |
|
|
|
WriteLine("[FieldOffset({0})]", field.OffsetInBytes); |
|
|
|
WriteLine("public {0} {1};", field.Type, |
|
SafeIdentifier(field.OriginalName)); |
|
|
|
NeedNewLine(); |
|
} |
|
|
|
foreach (var ctor in @class.Constructors) |
|
{ |
|
if (ctor.IsCopyConstructor || ctor.IsMoveConstructor) |
|
continue; |
|
|
|
NewLineIfNeeded(); |
|
GenerateFunction(ctor, @class); |
|
} |
|
|
|
foreach (var method in @class.Methods) |
|
{ |
|
if (CheckIgnoreMethod(@class, method)) |
|
continue; |
|
|
|
if (method.IsConstructor) |
|
continue; |
|
|
|
if (method.IsSynthetized) |
|
continue; |
|
|
|
NewLineIfNeeded(); |
|
GenerateFunction(method, @class); |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
NeedNewLine(); |
|
|
|
typePrinter.PopContext(); |
|
} |
|
|
|
private void GenerateStructMarshaling(Class @class) |
|
{ |
|
GenerateStructMarshalingFields(@class); |
|
} |
|
|
|
private void GenerateStructMarshalingFields(Class @class) |
|
{ |
|
foreach (var @base in @class.Bases) |
|
{ |
|
if (!@base.IsClass || @base.Class.Ignore) |
|
continue; |
|
|
|
GenerateStructMarshalingFields(@base.Class); |
|
} |
|
|
|
foreach (var field in @class.Fields) |
|
{ |
|
if (CheckIgnoreField(@class, field)) continue; |
|
|
|
bool isRefClass; |
|
var nativeField = GetFieldLocation(field, "native", |
|
CSharpTypePrinterContextKind.ManagedPointer, out isRefClass); |
|
|
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
Kind = CSharpMarshalKind.NativeField, |
|
ArgName = field.Name, |
|
ReturnVarName = nativeField, |
|
ReturnType = field.QualifiedType |
|
}; |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); |
|
field.Visit(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
WriteLine("{0} = {1};", field.Name, marshal.Context.Return); |
|
} |
|
} |
|
|
|
public bool ShouldGenerateClassNativeField(Class @class) |
|
{ |
|
if (!@class.IsRefType) |
|
return false; |
|
|
|
Class baseClass = null; |
|
|
|
if (@class.HasBaseClass) |
|
baseClass = @class.Bases[0].Class; |
|
|
|
var hasRefBase = baseClass != null && baseClass.IsRefType |
|
&& !baseClass.Ignore; |
|
|
|
var hasIgnoredBase = baseClass != null && baseClass.Ignore; |
|
|
|
return !@class.HasBase || !hasRefBase || hasIgnoredBase; |
|
} |
|
|
|
public void GenerateClassProlog(Class @class) |
|
{ |
|
if (@class.IsUnion) |
|
WriteLine("[StructLayout(LayoutKind.Explicit)]"); |
|
|
|
Write("public unsafe "); |
|
|
|
if (Options.GeneratePartialClasses) |
|
Write("partial "); |
|
|
|
Write(@class.IsValueType ? "struct " : "class "); |
|
Write("{0}", SafeIdentifier(@class.Name)); |
|
|
|
var needsBase = @class.HasBaseClass && !@class.IsValueType |
|
&& !@class.Bases[0].Class.IsValueType |
|
&& !@class.Bases[0].Class.Ignore; |
|
|
|
if (needsBase || @class.IsRefType) |
|
Write(" : "); |
|
|
|
if (needsBase) |
|
{ |
|
Write("{0}", SafeIdentifier(@class.Bases[0].Class.Name)); |
|
|
|
if (@class.IsRefType) |
|
Write(", "); |
|
} |
|
|
|
if (@class.IsRefType) |
|
Write("IDisposable"); |
|
} |
|
|
|
public void GenerateClassFields(Class @class) |
|
{ |
|
// Handle value-type inheritance |
|
if (@class.IsValueType) |
|
{ |
|
foreach (var @base in @class.Bases) |
|
{ |
|
Class baseClass; |
|
if (!@base.Type.IsTagDecl(out baseClass)) |
|
continue; |
|
|
|
if (!baseClass.IsValueType || baseClass.Ignore) |
|
continue; |
|
|
|
GenerateClassFields(baseClass); |
|
} |
|
} |
|
|
|
foreach (var field in @class.Fields) |
|
{ |
|
if (CheckIgnoreField(@class, field)) continue; |
|
|
|
NewLineIfNeeded(); |
|
|
|
if (@class.IsRefType) |
|
{ |
|
GenerateFieldProperty(field); |
|
} |
|
else |
|
{ |
|
GenerateDeclarationCommon(field); |
|
if (@class.IsUnion) |
|
WriteLine("[FieldOffset({0})]", field.Offset); |
|
WriteLine("public {0} {1};", field.Type, SafeIdentifier(field.Name)); |
|
} |
|
|
|
NeedNewLine(); |
|
} |
|
} |
|
|
|
private void GenerateFieldProperty(Field field) |
|
{ |
|
var @class = field.Class; |
|
|
|
GenerateDeclarationCommon(field); |
|
WriteLine("public {0} {1}", field.Type, SafeIdentifier(field.Name)); |
|
WriteStartBraceIndent(); |
|
|
|
GeneratePropertyGetter(field, @class); |
|
NewLine(); |
|
|
|
GeneratePropertySetter(field, @class); |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
enum PropertyMethodKind |
|
{ |
|
Getter, |
|
Setter |
|
} |
|
|
|
private string GetFieldLocation(Field field, string instance, |
|
CSharpTypePrinterContextKind kind, out bool isRefClass) |
|
{ |
|
isRefClass = false; |
|
|
|
var typePrinter = TypePrinter as CSharpTypePrinter; |
|
typePrinter.PushContext(kind); |
|
var type = field.Type.Visit(typePrinter); |
|
typePrinter.PopContext(); |
|
|
|
var fieldType = field.Type.Desugar(); |
|
|
|
Class fieldClass; |
|
if (fieldType.IsTagDecl(out fieldClass) && fieldClass.IsRefType) |
|
isRefClass = true; |
|
|
|
TagType tagType; |
|
if (fieldType.IsPointerTo(out tagType) |
|
&& tagType.IsTagDecl(out fieldClass) && fieldClass.IsRefType) |
|
isRefClass = true; |
|
|
|
if (CSharpTypePrinter.IsConstCharString(field.QualifiedType)) |
|
isRefClass = true; |
|
|
|
FunctionType functionType; |
|
if (fieldType.IsPointerTo(out functionType)) |
|
isRefClass = true; |
|
|
|
if (isRefClass) |
|
type = "void*"; |
|
|
|
var location = string.Format("({0} + {1})", instance, |
|
field.OffsetInBytes); |
|
|
|
if (type.TypeMap == null) |
|
location = string.Format("*({0}*){1}", type, location); |
|
|
|
return location; |
|
} |
|
|
|
private string GetPropertyLocation<T>(T decl, PropertyMethodKind kind, |
|
out bool isRefClass) where T : Declaration, ITypedDecl |
|
{ |
|
isRefClass = false; |
|
|
|
if (decl is Variable) |
|
{ |
|
var @var = decl as Variable; |
|
|
|
string symbol; |
|
if (!FindMangledDeclSymbol(@var, out symbol)) |
|
return string.Empty; |
|
|
|
NativeLibrary library; |
|
Driver.LibrarySymbols.FindLibraryBySymbol(symbol, out library); |
|
|
|
return string.Format("CppSharp.SymbolResolver.ResolveSymbol(\"{0}\", \"{1}\")", |
|
Path.GetFileNameWithoutExtension(library.FileName), symbol); |
|
} |
|
else if (decl is Field) |
|
{ |
|
var field = decl as Field; |
|
|
|
var location = GetFieldLocation(field, "Instance", |
|
CSharpTypePrinterContextKind.Managed, out isRefClass); |
|
|
|
if (isRefClass && kind == PropertyMethodKind.Getter) |
|
location = string.Format("new System.IntPtr({0})", location); |
|
|
|
return location; |
|
} |
|
|
|
throw new NotSupportedException(); |
|
} |
|
|
|
private void GeneratePropertySetter<T>(T decl, Class @class) |
|
where T : Declaration, ITypedDecl |
|
{ |
|
WriteLine("set"); |
|
WriteStartBraceIndent(); |
|
|
|
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; |
|
var parameters = new List<Parameter> { param }; |
|
GenerateInternalFunctionCall(function, @class, parameters); |
|
} |
|
else |
|
{ |
|
bool isRefClass; |
|
var variable = GetPropertyLocation(decl, PropertyMethodKind.Setter, |
|
out isRefClass); |
|
|
|
var marshal = new CSharpMarshalManagedToNativePrinter(ctx); |
|
param.Visit(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
Write("{0} = {1}", variable, marshal.Context.Return); |
|
|
|
if (isRefClass) |
|
Write(".ToPointer()"); |
|
|
|
WriteLine(";"); |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
private void GeneratePropertyGetter<T>(T decl, Class @class) |
|
where T : Declaration, ITypedDecl |
|
{ |
|
WriteLine("get"); |
|
WriteStartBraceIndent(); |
|
|
|
var @return = string.Empty; |
|
|
|
if (decl is Function) |
|
{ |
|
var function = decl as Function; |
|
GenerateInternalFunctionCall(function, @class); |
|
@return = "ret"; |
|
} |
|
else |
|
{ |
|
bool isRefClass; |
|
@return = GetPropertyLocation(decl, PropertyMethodKind.Getter, |
|
out isRefClass); |
|
|
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
ArgName = decl.Name, |
|
ReturnVarName = @return, |
|
ReturnType = decl.QualifiedType |
|
}; |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); |
|
decl.Visit(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
WriteLine("return {0};", marshal.Context.Return); |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
public void GenerateClassMethods(Class @class) |
|
{ |
|
var staticMethods = new List<Method>(); |
|
foreach (var method in @class.Methods) |
|
{ |
|
if (CheckIgnoreMethod(@class, method)) |
|
continue; |
|
|
|
if (method.IsConstructor) |
|
continue; |
|
|
|
if (method.IsStatic) |
|
{ |
|
staticMethods.Add(method); |
|
continue; |
|
} |
|
|
|
NewLineIfNeeded(); |
|
GenerateMethod(method, @class); |
|
NeedNewLine(); |
|
} |
|
|
|
foreach (var method in staticMethods) |
|
{ |
|
NewLineIfNeeded(); |
|
GenerateMethod(method, @class); |
|
NeedNewLine(); |
|
} |
|
} |
|
|
|
public void GenerateClassVariables(Class @class) |
|
{ |
|
foreach (var variable in @class.Variables) |
|
{ |
|
if (variable.Ignore) continue; |
|
|
|
if (variable.Access != AccessSpecifier.Public) |
|
continue; |
|
|
|
var type = variable.Type; |
|
|
|
NewLineIfNeeded(); |
|
GenerateVariable(@class, type, variable); |
|
NeedNewLine(); |
|
} |
|
} |
|
|
|
private void GenerateClassProperties(Class @class) |
|
{ |
|
foreach (var prop in @class.Properties) |
|
{ |
|
if (prop.Ignore) continue; |
|
|
|
NewLineIfNeeded(); |
|
WriteLine("public {0} {1}", prop.Type, prop.Name); |
|
WriteStartBraceIndent(); |
|
|
|
GeneratePropertyGetter(prop.GetMethod, @class); |
|
NeedNewLine(); |
|
|
|
if (prop.SetMethod != null) |
|
{ |
|
NewLineIfNeeded(); |
|
GeneratePropertySetter(prop.SetMethod, @class); |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
NeedNewLine(); |
|
} |
|
} |
|
|
|
private void GenerateVariable(Class @class, Type type, Variable variable) |
|
{ |
|
WriteLine("public static {0} {1}", type, variable.Name); |
|
WriteStartBraceIndent(); |
|
|
|
GeneratePropertyGetter(variable, @class); |
|
|
|
if (!variable.QualifiedType.Qualifiers.IsConst) |
|
GeneratePropertySetter(variable, @class); |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
#endregion |
|
|
|
#region Constructors |
|
|
|
public void GenerateClassConstructors(Class @class) |
|
{ |
|
NewLineIfNeeded(); |
|
|
|
// Output a default constructor that takes the native pointer. |
|
GenerateNativeConstructor(@class); |
|
NeedNewLine(); |
|
|
|
foreach (var ctor in @class.Constructors) |
|
{ |
|
if (CheckIgnoreMethod(@class, ctor)) |
|
continue; |
|
|
|
NewLineIfNeeded(); |
|
GenerateMethod(ctor, @class); |
|
NeedNewLine(); |
|
} |
|
|
|
if (@class.IsRefType) |
|
GenerateDisposeMethods(@class); |
|
} |
|
|
|
private void GenerateDisposeMethods(Class @class) |
|
{ |
|
var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; |
|
|
|
// Generate the IDispose Dispose() method. |
|
if (!hasBaseClass) |
|
{ |
|
NewLineIfNeeded(); |
|
WriteLine("public void Dispose()"); |
|
WriteStartBraceIndent(); |
|
|
|
WriteLine("Dispose(disposing: true);"); |
|
WriteLine("GC.SuppressFinalize(this);"); |
|
|
|
WriteCloseBraceIndent(); |
|
NeedNewLine(); |
|
} |
|
|
|
// Generate Dispose(bool) method |
|
NewLineIfNeeded(); |
|
Write("protected "); |
|
|
|
Write(hasBaseClass ? "override " : "virtual "); |
|
|
|
WriteLine("void Dispose(bool disposing)"); |
|
WriteStartBraceIndent(); |
|
|
|
if (ShouldGenerateClassNativeField(@class)) |
|
WriteLine("Marshal.FreeHGlobal(Instance);"); |
|
|
|
if (hasBaseClass) |
|
WriteLine("base.Dispose(disposing);"); |
|
|
|
WriteCloseBraceIndent(); |
|
NeedNewLine(); |
|
} |
|
|
|
private void GenerateNativeConstructor(Class @class) |
|
{ |
|
WriteLine("internal {0}(System.IntPtr native)", SafeIdentifier(@class.Name)); |
|
|
|
var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; |
|
if (hasBaseClass) |
|
WriteLineIndent(": base(native)"); |
|
|
|
WriteStartBraceIndent(); |
|
|
|
if (@class.IsRefType) |
|
{ |
|
if (ShouldGenerateClassNativeField(@class)) |
|
WriteLine("Instance = native;"); |
|
} |
|
else |
|
{ |
|
GenerateStructMarshaling(@class); |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
private bool GenerateClassConstructorBase(Class @class, Method method) |
|
{ |
|
var hasBase = @class.HasBase && !@class.Bases[0].Class.Ignore; |
|
|
|
if (hasBase && !@class.IsValueType) |
|
{ |
|
PushIndent(); |
|
Write(": this(", QualifiedIdentifier(@class.Bases[0].Class)); |
|
|
|
if (method != null) |
|
Write("IntPtr.Zero"); |
|
else |
|
Write("native"); |
|
|
|
WriteLine(")"); |
|
PopIndent(); |
|
} |
|
|
|
return hasBase; |
|
} |
|
|
|
#endregion |
|
|
|
#region Methods / Functions |
|
|
|
public void GenerateMethod(Method method, Class @class) |
|
{ |
|
GenerateDeclarationCommon(method); |
|
|
|
Write("public "); |
|
|
|
if (method.Kind == CXXMethodKind.Operator) |
|
Write("static "); |
|
|
|
var functionName = GetFunctionIdentifier(method, @class); |
|
|
|
if (method.IsConstructor || method.IsDestructor) |
|
Write("{0}(", functionName); |
|
else |
|
Write("{0} {1}(", method.ReturnType, functionName); |
|
|
|
GenerateMethodParameters(method, @class); |
|
|
|
WriteLine(")"); |
|
|
|
if (method.Kind == CXXMethodKind.Constructor) |
|
GenerateClassConstructorBase(@class, method); |
|
|
|
WriteStartBraceIndent(); |
|
|
|
if (@class.IsRefType) |
|
{ |
|
if (method.IsConstructor) |
|
{ |
|
GenerateClassConstructor(method, @class); |
|
} |
|
else if (method.IsOperator) |
|
{ |
|
GeneratedOperator(method, @class); |
|
} |
|
else |
|
{ |
|
GenerateInternalFunctionCall(method, @class); |
|
} |
|
} |
|
else if (@class.IsValueType) |
|
{ |
|
if (method.IsConstructor) |
|
{ |
|
GenerateInternalFunctionCall(method, @class); |
|
} |
|
else if (method.IsOperator) |
|
{ |
|
GeneratedOperator(method, @class); |
|
} |
|
else |
|
{ |
|
GenerateInternalFunctionCall(method, @class); |
|
} |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
private static string GetOperatorOverloadPair(CXXOperatorKind kind) |
|
{ |
|
switch (kind) |
|
{ |
|
case CXXOperatorKind.EqualEqual: |
|
return "!="; |
|
case CXXOperatorKind.ExclaimEqual: |
|
return "=="; |
|
|
|
case CXXOperatorKind.Less: |
|
return ">"; |
|
case CXXOperatorKind.Greater: |
|
return "<"; |
|
|
|
case CXXOperatorKind.LessEqual: |
|
return ">="; |
|
case CXXOperatorKind.GreaterEqual: |
|
return "<="; |
|
|
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
} |
|
|
|
private void GeneratedOperator(Method method, Class @class) |
|
{ |
|
if (method.IsSynthetized) |
|
{ |
|
var @operator = GetOperatorOverloadPair(method.OperatorKind); |
|
|
|
WriteLine("return !({0} {1} {2});", method.Parameters[0].Name, |
|
@operator, method.Parameters[1].Name); |
|
return; |
|
} |
|
|
|
GenerateInternalFunctionCall(method, @class); |
|
} |
|
|
|
private void GenerateClassConstructor(Method method, Class @class) |
|
{ |
|
var @params = GenerateFunctionParamsMarshal(method.Parameters, method); |
|
|
|
WriteLine("Instance = Marshal.AllocHGlobal({0});", @class.Layout.Size); |
|
Write("Internal.{0}(Instance", GetFunctionNativeIdentifier(method, @class)); |
|
if (@params.Any()) |
|
Write(", "); |
|
GenerateFunctionParams(@params); |
|
WriteLine(");"); |
|
} |
|
|
|
private void GenerateValueTypeConstructorCall(Method method, Class @class) |
|
{ |
|
var names = new List<string>(); |
|
|
|
foreach (var param in method.Parameters) |
|
{ |
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
Parameter = param, |
|
ArgName = param.Name, |
|
}; |
|
|
|
var marshal = new CSharpMarshalManagedToNativePrinter(ctx); |
|
param.Visit(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
names.Add(marshal.Context.Return); |
|
} |
|
|
|
WriteLine("var {0} = new {1}.Internal();", GeneratedIdentifier("instance"), |
|
@class.QualifiedName); |
|
|
|
GenerateInternalFunctionCall(method, @class); |
|
|
|
GenerateValueTypeConstructorCallFields(@class); |
|
} |
|
|
|
private void GenerateValueTypeConstructorCallFields(Class @class) |
|
{ |
|
foreach (var @base in @class.Bases) |
|
{ |
|
if (!@base.IsClass || @base.Class.Ignore) |
|
continue; |
|
|
|
var baseClass = @base.Class; |
|
GenerateValueTypeConstructorCallFields(baseClass); |
|
} |
|
|
|
foreach (var field in @class.Fields) |
|
{ |
|
if (CheckIgnoreField(@class, field)) continue; |
|
|
|
var nativeField = string.Format("*({0}*) (&{1} + {2})", |
|
field.Type, GeneratedIdentifier("instance"), field.OffsetInBytes); |
|
|
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
ReturnVarName = nativeField, |
|
ReturnType = field.QualifiedType |
|
}; |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); |
|
field.Visit(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
WriteLine("this.{0} = {1};", field.Name, marshal.Context.Return); |
|
} |
|
} |
|
|
|
public void GenerateInternalFunctionCall(Function function, Class @class, |
|
List<Parameter> parameters = null) |
|
{ |
|
if (parameters == null) |
|
parameters = function.Parameters; |
|
|
|
var functionName = string.Format("Internal.{0}", |
|
GetFunctionNativeIdentifier(function, @class)); |
|
GenerateFunctionCall(functionName, parameters, function, @class); |
|
} |
|
|
|
public void GenerateFunctionCall(string functionName, List<Parameter> parameters, |
|
Function function, Class @class = null) |
|
{ |
|
var retType = function.ReturnType; |
|
var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void); |
|
|
|
var method = function as Method; |
|
var needsInstance = method != null && !method.IsStatic && !method.IsOperator; |
|
|
|
var isValueType = @class != null && @class.IsValueType; |
|
var needsFixedThis = needsInstance && isValueType; |
|
|
|
Class retClass = null; |
|
if (function.HasHiddenStructParameter) |
|
{ |
|
function.ReturnType.Type.IsTagDecl(out retClass); |
|
|
|
WriteLine("var {0} = new {1}.Internal();", GeneratedIdentifier("udt"), |
|
retClass.OriginalName); |
|
|
|
retType.Type = new BuiltinType(PrimitiveType.Void); |
|
needsReturn = false; |
|
} |
|
|
|
var @params = GenerateFunctionParamsMarshal(parameters, function); |
|
|
|
var names = (from param in @params |
|
where !param.Param.Ignore |
|
select param.Name).ToList(); |
|
|
|
if (function.HasHiddenStructParameter) |
|
{ |
|
var name = string.Format("new IntPtr(&{0})", GeneratedIdentifier("udt")); |
|
names.Insert(0, name); |
|
} |
|
|
|
if (needsInstance) |
|
{ |
|
names.Insert(0, needsFixedThis ? string.Format("new System.IntPtr({0})", |
|
GeneratedIdentifier("instance")) : "Instance"); |
|
} |
|
|
|
if (needsFixedThis) |
|
{ |
|
WriteLine("fixed({0}* {1} = &this)", @class.QualifiedName, |
|
GeneratedIdentifier("instance")); |
|
WriteStartBraceIndent(); |
|
} |
|
|
|
if (needsReturn) |
|
Write("var ret = "); |
|
|
|
WriteLine("{0}({1});", functionName, string.Join(", ", names)); |
|
|
|
var cleanups = new List<TextGenerator>(); |
|
GenerateFunctionCallOutParams(@params, cleanups); |
|
|
|
foreach (var param in @params) |
|
{ |
|
var context = param.Context; |
|
if (context == null) continue; |
|
|
|
if (!string.IsNullOrWhiteSpace(context.Cleanup)) |
|
cleanups.Add(context.Cleanup); |
|
} |
|
|
|
foreach (var cleanup in cleanups) |
|
{ |
|
Write(cleanup); |
|
} |
|
|
|
if (needsReturn) |
|
{ |
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
ArgName = "ret", |
|
ReturnVarName = "ret", |
|
ReturnType = retType |
|
}; |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); |
|
function.ReturnType.Type.Visit(marshal, function.ReturnType.Qualifiers); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
WriteLine("return {0};", marshal.Context.Return); |
|
} |
|
|
|
if (function.HasHiddenStructParameter) |
|
{ |
|
WriteLine("var ret = new {0}();", retClass.Name); |
|
|
|
if (retClass.IsValueType) |
|
WriteLine("*({0}.Internal*) &ret = {1};", retClass.Name, |
|
GeneratedIdentifier("udt")); |
|
else |
|
WriteLine("*({0}.Internal*) ret.Instance.ToPointer() = {1};", |
|
retClass.Name, GeneratedIdentifier("udt")); |
|
|
|
WriteLine("return ret;"); |
|
} |
|
|
|
if (needsFixedThis) |
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
private void GenerateFunctionCallOutParams(IEnumerable<ParamMarshal> @params, |
|
ICollection<TextGenerator> cleanups) |
|
{ |
|
foreach (var paramInfo in @params) |
|
{ |
|
var param = paramInfo.Param; |
|
if (param.Usage != ParameterUsage.Out && param.Usage != ParameterUsage.InOut) |
|
continue; |
|
|
|
var nativeVarName = paramInfo.Name; |
|
|
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
ArgName = nativeVarName, |
|
ReturnVarName = nativeVarName, |
|
ReturnType = param.QualifiedType |
|
}; |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); |
|
param.Visit(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
WriteLine("{0} = {1};", param.Name, marshal.Context.Return); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.CSharpContext.Cleanup)) |
|
cleanups.Add(marshal.CSharpContext.Cleanup); |
|
} |
|
} |
|
|
|
private static bool IsInstanceFunction(Function function) |
|
{ |
|
var isInstanceFunction = false; |
|
|
|
var method = function as Method; |
|
if (method != null) |
|
isInstanceFunction = method.Conversion == MethodConversionKind.None; |
|
return isInstanceFunction; |
|
} |
|
|
|
public struct ParamMarshal |
|
{ |
|
public string Name; |
|
public Parameter Param; |
|
public CSharpMarshalContext Context; |
|
} |
|
|
|
public void GenerateFunctionParams(List<ParamMarshal> @params) |
|
{ |
|
var names = @params.Select(param => param.Name).ToList(); |
|
Write(string.Join(", ", names)); |
|
} |
|
|
|
public List<ParamMarshal> GenerateFunctionParamsMarshal(IEnumerable<Parameter> @params, |
|
Function function = null) |
|
{ |
|
var marshals = new List<ParamMarshal>(); |
|
|
|
var paramIndex = 0; |
|
foreach (var param in @params) |
|
{ |
|
marshals.Add(GenerateFunctionParamMarshal(param, paramIndex, function)); |
|
paramIndex++; |
|
} |
|
|
|
return marshals; |
|
} |
|
|
|
private ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramIndex, |
|
Function function = null) |
|
{ |
|
if (param.Type is BuiltinType) |
|
{ |
|
return new ParamMarshal { Name = param.Name, Param = param }; |
|
} |
|
|
|
var argName = "arg" + paramIndex.ToString(CultureInfo.InvariantCulture); |
|
var paramMarshal = new ParamMarshal { Name = argName, Param = param }; |
|
|
|
if (param.Usage == ParameterUsage.Out) |
|
{ |
|
//var paramType = param.Type; |
|
//if (paramType.IsReference()) |
|
// paramType = (paramType as PointerType).Pointee; |
|
|
|
//var typePrinter = new CppTypePrinter(Driver.TypeDatabase); |
|
//var type = paramType.Visit(typePrinter); |
|
|
|
//WriteLine("{0} {1};", type, argName); |
|
} |
|
else |
|
{ |
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
Parameter = param, |
|
ParameterIndex = paramIndex, |
|
ArgName = argName, |
|
Function = function |
|
}; |
|
|
|
paramMarshal.Context = ctx; |
|
|
|
var marshal = new CSharpMarshalManagedToNativePrinter(ctx); |
|
param.Visit(marshal); |
|
|
|
if (string.IsNullOrEmpty(marshal.Context.Return)) |
|
throw new Exception("Cannot marshal argument of function"); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
WriteLine("var {0} = {1};", SafeIdentifier(argName), marshal.Context.Return); |
|
} |
|
|
|
return paramMarshal; |
|
} |
|
|
|
private void GenerateMethodParameters(Method method, Class @class) |
|
{ |
|
var @params = new List<string>(); |
|
|
|
for (var i = 0; i < method.Parameters.Count; ++i) |
|
{ |
|
var param = method.Parameters[i]; |
|
|
|
if (param.Kind == ParameterKind.HiddenStructureReturn) |
|
continue; |
|
|
|
@params.Add(string.Format("{0} {1}", param.Type, SafeIdentifier(param.Name))); |
|
} |
|
|
|
Write(string.Join(", ", @params)); |
|
} |
|
|
|
#endregion |
|
|
|
public bool GenerateTypedef(TypedefDecl typedef) |
|
{ |
|
if (typedef.Ignore) |
|
return false; |
|
|
|
GenerateDeclarationCommon(typedef); |
|
|
|
FunctionType function; |
|
TagType tag; |
|
|
|
if (typedef.Type.IsPointerToPrimitiveType(PrimitiveType.Void) |
|
|| typedef.Type.IsPointerTo<TagType>(out tag)) |
|
{ |
|
WriteLine("public class " + SafeIdentifier(typedef.Name) + @" { }"); |
|
} |
|
else if (typedef.Type.IsPointerTo<FunctionType>(out function)) |
|
{ |
|
WriteLine("public {0};", |
|
string.Format(TypePrinter.VisitDelegate(function).Type, |
|
SafeIdentifier(typedef.Name))); |
|
NeedNewLine(); |
|
} |
|
else if (typedef.Type.IsEnumType()) |
|
{ |
|
// Already handled in the parser. |
|
return false; |
|
} |
|
else |
|
{ |
|
Console.WriteLine("Unhandled typedef type: {0}", typedef); |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public void GenerateEnum(Enumeration @enum) |
|
{ |
|
if (@enum.Ignore) return; |
|
GenerateDeclarationCommon(@enum); |
|
|
|
if (@enum.IsFlags) |
|
WriteLine("[Flags]"); |
|
|
|
Write("public enum {0}", SafeIdentifier(@enum.Name)); |
|
|
|
var typeName = TypePrinter.VisitPrimitiveType(@enum.BuiltinType.Type, |
|
new TypeQualifiers()); |
|
|
|
if (@enum.BuiltinType.Type != PrimitiveType.Int32) |
|
Write(" : {0}", typeName); |
|
|
|
NewLine(); |
|
|
|
WriteStartBraceIndent(); |
|
for (var i = 0; i < @enum.Items.Count; ++i) |
|
{ |
|
var item = @enum.Items[i]; |
|
GenerateInlineSummary(item.Comment); |
|
|
|
Write(item.ExplicitValue |
|
? string.Format("{0} = {1}", SafeIdentifier(item.Name), item.Value) |
|
: string.Format("{0}", SafeIdentifier(item.Name))); |
|
|
|
if (i < @enum.Items.Count - 1) |
|
Write(","); |
|
|
|
NewLine(); |
|
} |
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
public string GetOperatorIdentifier(CXXOperatorKind kind) |
|
{ |
|
// These follow the order described in MSDN (Overloadable Operators). |
|
|
|
switch (kind) |
|
{ |
|
// These unary operators can be overloaded |
|
case CXXOperatorKind.Plus: return "operator +"; |
|
case CXXOperatorKind.Minus: return "operator -"; |
|
case CXXOperatorKind.Exclaim: return "operator !"; |
|
case CXXOperatorKind.Tilde: return "operator ~"; |
|
case CXXOperatorKind.PlusPlus: return "operator ++"; |
|
case CXXOperatorKind.MinusMinus: return "operator --"; |
|
|
|
// These binary operators can be overloaded |
|
case CXXOperatorKind.Star: return "operator *"; |
|
case CXXOperatorKind.Slash: return "operator /"; |
|
case CXXOperatorKind.Percent: return "operator +"; |
|
case CXXOperatorKind.Amp: return "operator &"; |
|
case CXXOperatorKind.Pipe: return "operator |"; |
|
case CXXOperatorKind.Caret: return "operator ^"; |
|
case CXXOperatorKind.LessLess: return "operator <<"; |
|
case CXXOperatorKind.GreaterGreater: return "operator >>"; |
|
|
|
// The comparison operators can be overloaded |
|
case CXXOperatorKind.EqualEqual: return "operator =="; |
|
case CXXOperatorKind.ExclaimEqual: return "operator !="; |
|
case CXXOperatorKind.Less: return "operator <"; |
|
case CXXOperatorKind.Greater: return "operator >"; |
|
case CXXOperatorKind.LessEqual: return "operator <="; |
|
case CXXOperatorKind.GreaterEqual: return "operator >="; |
|
|
|
// Assignment operators cannot be overloaded |
|
case CXXOperatorKind.PlusEqual: |
|
case CXXOperatorKind.MinusEqual: |
|
case CXXOperatorKind.StarEqual: |
|
case CXXOperatorKind.SlashEqual: |
|
case CXXOperatorKind.PercentEqual: |
|
case CXXOperatorKind.AmpEqual: |
|
case CXXOperatorKind.PipeEqual: |
|
case CXXOperatorKind.CaretEqual: |
|
case CXXOperatorKind.LessLessEqual: |
|
case CXXOperatorKind.GreaterGreaterEqual: |
|
|
|
// The array indexing operator cannot be overloaded |
|
case CXXOperatorKind.Subscript: |
|
|
|
// The conditional logical operators cannot be overloaded |
|
case CXXOperatorKind.AmpAmp: |
|
case CXXOperatorKind.PipePipe: |
|
|
|
// These operators cannot be overloaded. |
|
case CXXOperatorKind.Equal: |
|
case CXXOperatorKind.Comma: |
|
case CXXOperatorKind.ArrowStar: |
|
case CXXOperatorKind.Arrow: |
|
case CXXOperatorKind.Call: |
|
case CXXOperatorKind.Conditional: |
|
case CXXOperatorKind.New: |
|
case CXXOperatorKind.Delete: |
|
case CXXOperatorKind.Array_New: |
|
case CXXOperatorKind.Array_Delete: |
|
default: throw new NotImplementedException(); |
|
} |
|
} |
|
|
|
public string GetFunctionIdentifier(Function function, Class @class) |
|
{ |
|
var identifier = SafeIdentifier(function.Name); |
|
|
|
var printer = TypePrinter as CSharpTypePrinter; |
|
var isNativeContext = printer.ContextKind == CSharpTypePrinterContextKind.Native; |
|
|
|
var method = function as Method; |
|
if (method != null && method.Kind == CXXMethodKind.Operator) |
|
{ |
|
if (isNativeContext) |
|
identifier = "Operator" + method.OperatorKind.ToString(); |
|
else |
|
identifier = GetOperatorIdentifier(method.OperatorKind); |
|
} |
|
|
|
var overloads = AST.Utils.GetFunctionOverloads(function, @class); |
|
|
|
var index = overloads.IndexOf(function); |
|
if (isNativeContext && index >= 0) |
|
identifier += index.ToString(); |
|
|
|
return identifier; |
|
} |
|
|
|
public string GetFunctionNativeIdentifier(Function function, Class @class = null) |
|
{ |
|
var typePrinter = TypePrinter as CSharpTypePrinter; |
|
typePrinter.PushContext(CSharpTypePrinterContextKind.Native); |
|
|
|
var name = GetFunctionIdentifier(function, @class); |
|
|
|
typePrinter.PopContext(); |
|
|
|
return name; |
|
} |
|
|
|
bool FindMangledDeclLibrary(IMangledDecl decl, out NativeLibrary library) |
|
{ |
|
string symbol; |
|
if (!FindMangledDeclSymbol(decl, out symbol)) |
|
{ |
|
library = null; |
|
return false; |
|
} |
|
|
|
Driver.LibrarySymbols.FindLibraryBySymbol(symbol, out library); |
|
return true; |
|
} |
|
|
|
bool FindMangledDeclSymbol(IMangledDecl decl, out string symbol) |
|
{ |
|
symbol = decl.Mangled; |
|
if (!Driver.LibrarySymbols.FindSymbol(ref symbol)) |
|
{ |
|
Driver.Diagnostics.EmitError(DiagnosticId.SymbolNotFound, |
|
"Symbol not found: {0}", symbol); |
|
symbol = null; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public void GenerateFunction(Function function, Class @class = null) |
|
{ |
|
if(function.Ignore) return; |
|
|
|
NativeLibrary library; |
|
if (!FindMangledDeclLibrary(function, out library)) |
|
return; |
|
|
|
GenerateDeclarationCommon(function); |
|
|
|
WriteLine("[SuppressUnmanagedCodeSecurity]"); |
|
Write("[DllImport(\"{0}\", ", library.FileName); |
|
WriteLine("CallingConvention = CallingConvention.{0},", |
|
Helpers.ToCSharpCallConv(function.CallingConvention)); |
|
WriteLineIndent("EntryPoint=\"{0}\")]", function.Mangled); |
|
|
|
if (function.ReturnType.Type.Desugar().IsPrimitiveType(PrimitiveType.Bool)) |
|
WriteLine("[return: MarshalAsAttribute(UnmanagedType.I1)]"); |
|
|
|
var @params = new List<string>(); |
|
|
|
var typePrinter = TypePrinter as CSharpTypePrinter; |
|
var retType = typePrinter.VisitParameterDecl(new Parameter() |
|
{ |
|
QualifiedType = function.ReturnType |
|
}); |
|
|
|
var method = function as Method; |
|
if (method != null && !method.IsStatic) |
|
{ |
|
@params.Add("System.IntPtr instance"); |
|
|
|
if (method.IsConstructor && Options.IsMicrosoftAbi) |
|
retType = "System.IntPtr"; |
|
} |
|
|
|
for(var i = 0; i < function.Parameters.Count; ++i) |
|
{ |
|
var param = function.Parameters[i]; |
|
|
|
if (param.Kind == ParameterKind.OperatorParameter) |
|
continue; |
|
|
|
var typeName = param.Visit(typePrinter); |
|
|
|
var paramName = param.IsSynthetized ? |
|
GeneratedIdentifier(param.Name) : SafeIdentifier(param.Name); |
|
|
|
@params.Add(string.Format("{0} {1}", typeName, paramName)); |
|
} |
|
|
|
if (method != null && method.IsConstructor) |
|
if (Options.IsMicrosoftAbi && @class.Layout.HasVirtualBases) |
|
@params.Add("int " + GeneratedIdentifier("forBases")); |
|
|
|
WriteLine("public unsafe static extern {0} {1}({2});", retType, |
|
GetFunctionIdentifier(function, @class), |
|
string.Join(", ", @params)); |
|
} |
|
} |
|
|
|
internal class SymbolNotFoundException : Exception |
|
{ |
|
public SymbolNotFoundException(string msg) : base(msg) |
|
{} |
|
} |
|
} |