Browse Source

Added a massively improved C# backend that is based on the binding logic of the CLI backend.

pull/1/head
triton 12 years ago
parent
commit
a28eeaaf0d
  1. 8
      src/Bridge/Type.cs
  2. 555
      src/Generator/Generators/CSharp/CSharpMarshal.cs
  3. 818
      src/Generator/Generators/CSharp/CSharpTextTemplate.cs
  4. 87
      src/Generator/Generators/CSharp/CSharpTypePrinter.cs
  5. 3
      src/Generator/Types/TypeMap.cs

8
src/Bridge/Type.cs

@ -138,6 +138,11 @@ namespace Cxxi
/// </summary> /// </summary>
public struct QualifiedType public struct QualifiedType
{ {
public QualifiedType(Type type) : this()
{
Type = type;
}
public Type Type { get; set; } public Type Type { get; set; }
public TypeQualifiers Qualifiers { get; set; } public TypeQualifiers Qualifiers { get; set; }
@ -402,7 +407,8 @@ namespace Cxxi
Int64, Int64,
UInt64, UInt64,
Float, Float,
Double Double,
IntPtr
} }
/// <summary> /// <summary>

555
src/Generator/Generators/CSharp/CSharpMarshal.cs

@ -0,0 +1,555 @@
using System.Text;
using Cxxi.Generators.CLI;
using Cxxi.Types;
namespace Cxxi.Generators.CSharp
{
public class CSharpMarshalContext : MarshalContext
{
public CSharpMarshalContext(Driver driver)
: base(driver)
{
Cleanup = new TextGenerator();
}
public TextGenerator Cleanup { get; private set; }
}
public abstract class CSharpMarshalPrinter : MarshalPrinter
{
public CSharpMarshalContext CSharpContext
{
get { return Context as CSharpMarshalContext; }
}
protected CSharpMarshalPrinter(CSharpMarshalContext context)
: base(context)
{
}
}
public class CSharpMarshalNativeToManagedPrinter : CSharpMarshalPrinter
{
public CSharpMarshalNativeToManagedPrinter(CSharpMarshalContext context)
: base(context)
{
Context.MarshalToManaged = this;
}
public override bool VisitTagType(TagType tag, TypeQualifiers quals)
{
var decl = tag.Declaration;
return decl.Visit(this);
}
public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
{
switch (array.SizeType)
{
case ArrayType.ArraySize.Constant:
Context.Return.Write("null");
break;
case ArrayType.ArraySize.Variable:
Context.Return.Write("null");
break;
}
return true;
}
public override bool VisitFunctionType(FunctionType function, TypeQualifiers quals)
{
var returnType = function.ReturnType;
return returnType.Visit(this, quals);
}
public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals)
{
var pointee = pointer.Pointee;
if (pointee.IsPrimitiveType(PrimitiveType.Void, walkTypedefs: true))
{
Context.Return.Write("new IntPtr({0})", Context.ReturnVarName);
return true;
}
if (pointee.IsPrimitiveType(PrimitiveType.Char))
{
Context.Return.Write("Marshal.StringToHGlobalAnsi({0})",
Context.ReturnVarName);
return true;
}
PrimitiveType primitive;
if (pointee.IsPrimitiveType(out primitive, walkTypedefs: true))
{
Context.Return.Write("new IntPtr({0})", Context.ReturnVarName);
return true;
}
if (!pointee.Visit(this, quals))
return false;
return true;
}
public override bool VisitMemberPointerType(MemberPointerType member,
TypeQualifiers quals)
{
return false;
}
public override bool VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals)
{
return VisitPrimitiveType(builtin.Type);
}
public bool VisitPrimitiveType(PrimitiveType primitive)
{
switch (primitive)
{
case PrimitiveType.Void:
return true;
case PrimitiveType.Bool:
case PrimitiveType.Int8:
case PrimitiveType.UInt8:
case PrimitiveType.Int16:
case PrimitiveType.UInt16:
case PrimitiveType.Int32:
case PrimitiveType.UInt32:
case PrimitiveType.Int64:
case PrimitiveType.UInt64:
case PrimitiveType.Float:
case PrimitiveType.Double:
Context.Return.Write(Context.ReturnVarName);
return true;
case PrimitiveType.WideChar:
return false;
}
return false;
}
public override bool VisitTypedefType(TypedefType typedef, TypeQualifiers quals)
{
var decl = typedef.Declaration;
TypeMap typeMap = null;
if (Context.Driver.TypeDatabase.FindTypeMap(decl, out typeMap))
{
typeMap.Type = typedef;
typeMap.CLIMarshalToManaged(Context);
return typeMap.IsValueType;
}
FunctionType function;
if (decl.Type.IsPointerTo(out function))
{
Context.Return.Write("safe_cast<{0}>(", typedef);
Context.Return.Write("System::Runtime::InteropServices::Marshal::");
Context.Return.Write("GetDelegateForFunctionPointer(");
Context.Return.Write("IntPtr({0}), {1}::typeid))", Context.ReturnVarName,
typedef.ToString().TrimEnd('^'));
return true;
}
return decl.Type.Visit(this);
}
public override bool VisitTemplateSpecializationType(TemplateSpecializationType template,
TypeQualifiers quals)
{
TypeMap typeMap;
if (Context.Driver.TypeDatabase.FindTypeMap(template, out typeMap))
{
typeMap.Type = template;
typeMap.CSharpMarshalToManaged(Context);
return true;
}
return template.Template.Visit(this);
}
public override bool VisitClassDecl(Class @class)
{
var instance = string.Empty;
if (!Context.ReturnType.IsPointer())
instance += "&";
instance += Context.ReturnVarName;
WriteClassInstance(@class, instance);
return true;
}
public string QualifiedIdentifier(Declaration decl)
{
if (Context.Driver.Options.GenerateLibraryNamespace)
return string.Format("{0}::{1}", Context.Driver.Options.OutputNamespace,
decl.QualifiedName);
return string.Format("{0}", decl.QualifiedName);
}
public void WriteClassInstance(Class @class, string instance)
{
if (@class.IsRefType)
Context.Return.Write("gcnew ");
Context.Return.Write("{0}(", QualifiedIdentifier(@class));
Context.Return.Write("(::{0}*)", @class.QualifiedOriginalName);
Context.Return.Write("{0})", instance);
}
public override bool VisitFieldDecl(Field field)
{
return field.Type.Visit(this);
}
public override bool VisitParameterDecl(Parameter parameter)
{
return parameter.Type.Visit(this, parameter.QualifiedType.Qualifiers);
}
public override bool VisitEnumDecl(Enumeration @enum)
{
Context.Return.Write("({0}){1}", ToCLITypeName(@enum),
Context.ReturnVarName);
return true;
}
public override bool VisitVariableDecl(Variable variable)
{
return variable.Type.Visit(this, variable.QualifiedType.Qualifiers);
}
private string ToCLITypeName(Declaration decl)
{
var typePrinter = new CLITypePrinter(Context.Driver);
return typePrinter.VisitDeclaration(decl);
}
public override bool VisitClassTemplateDecl(ClassTemplate template)
{
return template.TemplatedClass.Visit(this);
}
}
public class CSharpMarshalManagedToNativePrinter : CSharpMarshalPrinter
{
public CSharpMarshalManagedToNativePrinter(CSharpMarshalContext context)
: base(context)
{
Context.MarshalToNative = this;
}
public override bool VisitFunctionType(FunctionType function, TypeQualifiers quals)
{
var returnType = function.ReturnType;
return returnType.Visit(this, quals);
}
public bool VisitDelegateType(FunctionType function, string type)
{
// We marshal function pointer types by calling
// GetFunctionPointerForDelegate to get a native function
// pointer ouf of the delegate. Then we can pass it in the
// native call. Since references are not tracked in the
// native side, we need to be careful and protect it with an
// explicit GCHandle if necessary.
var sb = new StringBuilder();
sb.AppendFormat("static_cast<::{0}>(", type);
sb.Append("System::Runtime::InteropServices::Marshal::");
sb.Append("GetFunctionPointerForDelegate(");
sb.AppendFormat("{0}).ToPointer())", Context.Parameter.Name);
Context.Return.Write(sb.ToString());
return true;
}
public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals)
{
var pointee = pointer.Pointee;
var isVoidPtr = pointee.IsPrimitiveType(PrimitiveType.Void,
walkTypedefs: true);
var isUInt8Ptr = pointee.IsPrimitiveType(PrimitiveType.UInt8,
walkTypedefs: true);
if (isVoidPtr || isUInt8Ptr)
{
if (isUInt8Ptr)
Context.Return.Write("({0})", "uint8*");
Context.Return.Write("{0}.ToPointer()", Context.Parameter.Name);
return true;
}
if (pointee.IsPrimitiveType(PrimitiveType.Char) ||
pointee.IsPrimitiveType(PrimitiveType.WideChar))
{
Context.Return.Write("Marshal.StringToHGlobalAnsi({0})",
Context.Parameter.Name);
CSharpContext.Cleanup.WriteLine("Marshal.FreeHGlobal({0});",
Context.ArgName);
return true;
}
if (pointee is FunctionType)
{
var function = pointee as FunctionType;
// TODO: We have to translate the function type typedef to C/C++
return VisitDelegateType(function, function.ToString());
}
Class @class;
if (pointee.IsTagDecl(out @class))
{
if (@class.IsRefType)
Context.Return.Write("{0}.Instance", Context.Parameter.Name);
else
Context.Return.Write("new IntPtr(&{0})", Context.Parameter.Name);
return true;
}
return pointee.Visit(this, quals);
}
public override bool VisitMemberPointerType(MemberPointerType member,
TypeQualifiers quals)
{
return false;
}
public override bool VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals)
{
return VisitPrimitiveType(builtin.Type);
}
public bool VisitPrimitiveType(PrimitiveType primitive)
{
switch (primitive)
{
case PrimitiveType.Void:
return true;
case PrimitiveType.Bool:
case PrimitiveType.Int8:
case PrimitiveType.UInt8:
case PrimitiveType.Int16:
case PrimitiveType.UInt16:
case PrimitiveType.Int32:
case PrimitiveType.UInt32:
case PrimitiveType.Int64:
case PrimitiveType.UInt64:
case PrimitiveType.Float:
case PrimitiveType.Double:
Context.Return.Write(Context.Parameter.Name);
return true;
case PrimitiveType.WideChar:
return false;
}
return false;
}
public override bool VisitTypedefType(TypedefType typedef, TypeQualifiers quals)
{
var decl = typedef.Declaration;
TypeMap typeMap = null;
if (Context.Driver.TypeDatabase.FindTypeMap(decl, out typeMap))
{
typeMap.CSharpMarshalToNative(Context);
return typeMap.IsValueType;
}
FunctionType func;
if (decl.Type.IsPointerTo<FunctionType>(out func))
{
VisitDelegateType(func, typedef.Declaration.OriginalName);
return true;
}
PrimitiveType primitive;
if (decl.Type.IsPrimitiveType(out primitive, walkTypedefs: true))
{
Context.Return.Write("({0})", typedef.Declaration.Name);
}
return decl.Type.Visit(this);
}
public override bool VisitTemplateSpecializationType(TemplateSpecializationType template,
TypeQualifiers quals)
{
TypeMap typeMap = null;
if (Context.Driver.TypeDatabase.FindTypeMap(template, out typeMap))
{
typeMap.Type = template;
typeMap.CSharpMarshalToNative(Context);
return true;
}
return template.Template.Visit(this);
}
public override bool VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals)
{
Context.Return.Write(param.Parameter.Name);
return true;
}
public override bool VisitClassDecl(Class @class)
{
if (@class.IsValueType)
{
MarshalValueClass(@class);
}
else
{
MarshalRefClass(@class);
}
return true;
}
private void MarshalRefClass(Class @class)
{
TypeMap typeMap = null;
if (Context.Driver.TypeDatabase.FindTypeMap(@class, out typeMap))
{
typeMap.CLIMarshalToNative(Context);
return;
}
var method = Context.Function as Method;
if (method != null
&& method.Conversion == MethodConversionKind.FunctionToInstanceMethod
&& Context.ParameterIndex == 0)
{
Context.Return.Write("Instance");
return;
}
if (Context.Parameter.Type.IsPointer())
Context.Return.Write("{0}.Instance", Context.Parameter.Name);
else
Context.Return.Write("{0}", Context.Parameter.Name);
}
private void MarshalValueClass(Class @class)
{
var marshalVar = "_marshal" + Context.ParameterIndex++;
Context.SupportBefore.WriteLine("auto {0} = ::{1}();", marshalVar,
@class.QualifiedOriginalName);
MarshalValueClassFields(@class, marshalVar);
Context.Return.Write(marshalVar);
}
private void MarshalValueClassFields(Class @class, string marshalVar)
{
foreach (var @base in @class.Bases)
{
if (!@base.IsClass || @base.Class.Ignore)
continue;
var baseClass = @base.Class;
MarshalValueClassFields(baseClass, marshalVar);
}
foreach (var field in @class.Fields)
{
if (field.Ignore)
continue;
MarshalValueClassField(field, marshalVar);
}
}
private void MarshalValueClassField(Field field, string marshalVar)
{
var fieldRef = string.Format("{0}.{1}", Context.Parameter.Name,
field.Name);
var marshalCtx = new MarshalContext(Context.Driver)
{
ArgName = fieldRef,
ParameterIndex = Context.ParameterIndex++
};
var marshal = new CLIMarshalManagedToNativePrinter(marshalCtx);
field.Visit(marshal);
Context.ParameterIndex = marshalCtx.ParameterIndex;
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
Context.SupportBefore.Write(marshal.Context.SupportBefore);
if (field.Type.IsPointer())
{
Context.SupportBefore.WriteLine("if ({0} != nullptr)", fieldRef);
Context.SupportBefore.PushIndent();
}
Context.SupportBefore.WriteLine("{0}.{1} = {2};", marshalVar, field.OriginalName,
marshal.Context.Return);
if (field.Type.IsPointer())
Context.SupportBefore.PopIndent();
}
public override bool VisitFieldDecl(Field field)
{
Context.Parameter = new Parameter
{
Name = Context.ArgName,
QualifiedType = field.QualifiedType
};
return field.Type.Visit(this);
}
public override bool VisitParameterDecl(Parameter parameter)
{
var paramType = parameter.Type;
Class @class;
if (paramType.Desugar().IsTagDecl(out @class))
{
if (@class.IsRefType)
{
Context.Return.Write(
"*({0}.Internal*){1}.Instance.ToPointer()",
@class.Name, parameter.Name);
return true;
}
else
{
Context.Return.Write("*({0}.Internal*)&{1}",
@class.Name, parameter.Name);
return true;
}
}
return paramType.Visit(this);
}
public override bool VisitEnumDecl(Enumeration @enum)
{
Context.Return.Write("(::{0}){1}", @enum.QualifiedOriginalName,
Context.Parameter.Name);
return true;
}
public override bool VisitClassTemplateDecl(ClassTemplate template)
{
return template.TemplatedClass.Visit(this);
}
}
}

818
src/Generator/Generators/CSharp/CSharpTextTemplate.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using Cxxi.Types; using Cxxi.Types;
@ -9,12 +10,19 @@ namespace Cxxi.Generators.CSharp
{ {
public ITypePrinter TypePrinter { get; set; } public ITypePrinter TypePrinter { get; set; }
public override string FileExtension
{
get { return "cs"; }
}
public CSharpTextTemplate(Driver driver, TranslationUnit unit) public CSharpTextTemplate(Driver driver, TranslationUnit unit)
: base(driver, unit) : base(driver, unit)
{ {
TypePrinter = new CSharpTypePrinter(driver.TypeDatabase, driver.Library); TypePrinter = new CSharpTypePrinter(driver.TypeDatabase, driver.Library);
} }
#region Identifiers
// from https://github.com/mono/mono/blob/master/mcs/class/System/Microsoft.CSharp/CSharpCodeGenerator.cs // from https://github.com/mono/mono/blob/master/mcs/class/System/Microsoft.CSharp/CSharpCodeGenerator.cs
private static string[] keywords = new string[] private static string[] keywords = new string[]
{ {
@ -32,6 +40,11 @@ namespace Cxxi.Generators.CSharp
"partial", "yield", "where" "partial", "yield", "where"
}; };
public string GeneratedIdentifier(string id)
{
return "__" + id;
}
public static string SafeIdentifier(string proposedName) public static string SafeIdentifier(string proposedName)
{ {
proposedName = proposedName =
@ -39,17 +52,41 @@ namespace Cxxi.Generators.CSharp
return keywords.Contains(proposedName) ? "@" + proposedName : proposedName; return keywords.Contains(proposedName) ? "@" + proposedName : proposedName;
} }
public override string FileExtension public string QualifiedIdentifier(Declaration decl)
{ {
get { return "cs"; } if (Options.GenerateLibraryNamespace)
return string.Format("{0}::{1}", Options.OutputNamespace, decl.QualifiedName);
return string.Format("{0}", decl.QualifiedName);
}
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";
}
#endregion
public override void Generate() public override void Generate()
{ {
GenerateStart(); GenerateStart();
WriteLine("using System;"); WriteLine("using System;");
WriteLine("using System.Runtime.InteropServices;"); WriteLine("using System.Runtime.InteropServices;");
WriteLine("using System.Security;");
NewLine(); NewLine();
if (Options.GenerateLibraryNamespace) if (Options.GenerateLibraryNamespace)
@ -58,7 +95,7 @@ namespace Cxxi.Generators.CSharp
WriteStartBraceIndent(); WriteStartBraceIndent();
} }
GenerateDeclarations(); GenerateNamespace(TranslationUnit);
if (Options.GenerateLibraryNamespace) if (Options.GenerateLibraryNamespace)
WriteCloseBraceIndent(); WriteCloseBraceIndent();
@ -78,11 +115,6 @@ namespace Cxxi.Generators.CSharp
WriteLine("//----------------------------------------------------------------------------"); WriteLine("//----------------------------------------------------------------------------");
} }
public void GenerateDeclarations()
{
GenerateNamespace(TranslationUnit);
}
private void GenerateNamespace(Namespace @namespace) private void GenerateNamespace(Namespace @namespace)
{ {
bool isGlobalNamespace = @namespace is TranslationUnit; bool isGlobalNamespace = @namespace is TranslationUnit;
@ -142,26 +174,70 @@ namespace Cxxi.Generators.CSharp
WriteCloseBraceIndent(); WriteCloseBraceIndent();
} }
public void GenerateDeclarationCommon(Declaration T) 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)
{ {
GenerateSummary(T.BriefComment); if (String.IsNullOrWhiteSpace(comment))
GenerateDebug(T); return;
WriteLine("/// <summary>{0}</summary>", comment);
} }
#region Classes #region Classes
public void GenerateClass(Class @class) public void GenerateClass(Class @class)
{ {
if (@class.Ignore) return; if (@class.Ignore || @class.IsIncomplete)
return;
GenerateDeclarationCommon(@class); GenerateDeclarationCommon(@class);
if (@class.IsUnion)
{
// TODO: How to do wrapping of unions?
throw new NotImplementedException();
}
GenerateClassProlog(@class); GenerateClassProlog(@class);
NewLine(); NewLine();
WriteStartBraceIndent(); WriteStartBraceIndent();
WriteLine("const string DllName = \"{0}.dll\";", Library.Name);
NewLine();
if (!@class.IsOpaque) if (!@class.IsOpaque)
{ {
GenerateClassInternals(@class);
if (ShouldGenerateClassNativeField(@class))
{
WriteLine("public System.IntPtr Instance { get; protected set; }");
NewLine();
}
GenerateClassConstructors(@class);
GenerateClassFields(@class); GenerateClassFields(@class);
GenerateClassMethods(@class); GenerateClassMethods(@class);
} }
@ -169,6 +245,123 @@ namespace Cxxi.Generators.CSharp
WriteCloseBraceIndent(); WriteCloseBraceIndent();
} }
public void GenerateClassInternals(Class @class)
{
var typePrinter = Type.TypePrinter as CSharpTypePrinter;
typePrinter.PushContext(CSharpTypePrinterContextKind.Native);
WriteLine("[StructLayout(LayoutKind.Explicit, Size = {0})]",
@class.Layout.Size);
WriteLine("internal new struct Internal");
WriteStartBraceIndent();
ResetNewLine();
foreach (var field in @class.Fields)
{
NewLineIfNeeded();
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();
GeneratePInvokeMethod(ctor, @class);
NeedNewLine();
}
foreach (var method in @class.Methods)
{
if (CheckIgnoreMethod(@class, method))
continue;
if (method.IsConstructor)
continue;
NewLineIfNeeded();
GeneratePInvokeMethod(method, @class);
NeedNewLine();
}
WriteCloseBraceIndent();
NewLine();
typePrinter.PopContext();
}
private void GeneratePInvokeMethod(Method method, Class @class)
{
GenerateFunction(method, @class);
}
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;
var nativeField = string.Format("*({0}*) (native + {1})",
field.Type, field.OffsetInBytes);
var ctx = new CSharpMarshalContext(Driver)
{
ArgName = field.Name,
ReturnVarName = nativeField,
ReturnType = field.Type
};
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) public void GenerateClassProlog(Class @class)
{ {
if (@class.IsUnion) if (@class.IsUnion)
@ -179,34 +372,546 @@ namespace Cxxi.Generators.CSharp
if (@class.IsAbstract) if (@class.IsAbstract)
Write("abstract "); Write("abstract ");
Write("class {0}", SafeIdentifier(@class.Name)); Write(@class.IsValueType ? "struct " : "class ");
Write("{0}", SafeIdentifier(@class.Name));
var needsBase = @class.HasBase && !@class.IsValueType
&& !@class.Bases[0].Class.IsValueType
&& !@class.Bases[0].Class.Ignore;
if (@class.HasBase) if (needsBase || @class.IsRefType)
Write(" : {0}", SafeIdentifier(@class.Bases[0].Class.Name)); 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) 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);
}
}
ResetNewLine();
foreach (var field in @class.Fields) foreach (var field in @class.Fields)
{ {
if (field.Ignore) continue; if (CheckIgnoreField(@class, field)) continue;
NewLineIfNeeded();
if (@class.IsRefType)
{
GenerateFieldProperty(field);
}
else
{
GenerateDeclarationCommon(field); GenerateDeclarationCommon(field);
if (@class.IsUnion) if (@class.IsUnion)
WriteLine("[FieldOffset({0})]", field.Offset); WriteLine("[FieldOffset({0})]", field.Offset);
WriteLine("public {0} {1};", field.Type, SafeIdentifier(field.Name)); 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();
}
private string GetPropertyLocation<T>(T decl, Class @class)
where T : Declaration, ITypedDecl
{
if (decl is Variable)
{
return string.Format("::{0}::{1}",
@class.QualifiedOriginalName, decl.OriginalName);
}
var field = decl as Field;
return string.Format("*({0}*) (Instance + {1})", field.Type,
field.OffsetInBytes);
}
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,
};
var marshal = new CSharpMarshalManagedToNativePrinter(ctx);
param.Visit(marshal);
var variable = GetPropertyLocation(decl, @class);
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
Write(marshal.Context.SupportBefore);
WriteLine("{0} = {1};", variable, marshal.Context.Return);
WriteCloseBraceIndent();
}
private void GeneratePropertyGetter<T>(T decl, Class @class)
where T : Declaration, ITypedDecl
{
WriteLine("get");
WriteStartBraceIndent();
var variable = GetPropertyLocation(decl, @class);
var ctx = new CSharpMarshalContext(Driver)
{
ArgName = decl.Name,
ReturnVarName = variable,
ReturnType = decl.Type
};
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) public void GenerateClassMethods(Class @class)
{ {
ResetNewLine();
var staticMethods = new List<Method>();
foreach (var method in @class.Methods) foreach (var method in @class.Methods)
GenerateClassMethod(method, @class); {
if (CheckIgnoreMethod(@class, method))
continue;
if (method.IsConstructor)
continue;
if (method.IsStatic)
{
staticMethods.Add(method);
continue;
}
NewLineIfNeeded();
GenerateMethod(method, @class);
NeedNewLine();
} }
public void GenerateClassMethod(Method method, Class @class) ResetNewLine();
foreach (var method in staticMethods)
{ {
GenerateFunction(method); NewLineIfNeeded();
GenerateMethod(method, @class);
NeedNewLine();
}
}
#endregion
#region Constructors
public void GenerateClassConstructors(Class @class)
{
// Output a default constructor that takes the native pointer.
WriteLine("{0}(System.IntPtr native)", SafeIdentifier(@class.Name));
WriteStartBraceIndent();
if (@class.IsRefType)
{
if (ShouldGenerateClassNativeField(@class))
WriteLine("Instance = native;");
}
else
{
GenerateStructMarshaling(@class);
}
WriteCloseBraceIndent();
NewLine();
foreach (var ctor in @class.Constructors)
{
if (ctor.IsCopyConstructor || ctor.IsMoveConstructor)
continue;
// Default constructors are not supported in .NET value types.
if (ctor.Parameters.Count == 0 && @class.IsValueType)
continue;
GenerateMethod(ctor, @class);
NewLine();
}
if (@class.IsRefType)
{
WriteLine("public void Dispose()");
WriteStartBraceIndent();
if (ShouldGenerateClassNativeField(@class))
WriteLine("Marshal.FreeHGlobal(Instance);");
WriteCloseBraceIndent();
NewLine();
}
}
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.Constructor || method.Kind == CXXMethodKind.Destructor)
Write("{0}(", SafeIdentifier(method.Name));
else
Write("{0} {1}(", method.ReturnType, SafeIdentifier(method.Name));
GenerateMethodParameters(method);
WriteLine(")");
if (method.Kind == CXXMethodKind.Constructor)
GenerateClassConstructorBase(@class, method);
WriteStartBraceIndent();
if (@class.IsRefType)
{
if (method.Kind == CXXMethodKind.Constructor)
{
if (!@class.IsAbstract)
{
var @params = GenerateFunctionParamsMarshal(method.Parameters, method);
WriteLine("Instance = Marshal.AllocHGlobal({0});", @class.Layout.Size);
Write("Internal.{0}(Instance", method.Name);
GenerateFunctionParams(@params);
WriteLine(");");
}
}
else
{
GenerateFunctionCall(method, @class);
}
}
else if (@class.IsValueType)
{
//if (method.Kind != CXXMethodKind.Constructor)
// GenerateFunctionCall(method, @class);
//else
// GenerateValueTypeConstructorCall(method, @class);
}
WriteCloseBraceIndent();
}
public void GenerateFunctionCall(Function function, Class @class = null)
{
var retType = function.ReturnType;
var needsReturn = !retType.IsPrimitiveType(PrimitiveType.Void);
var isValueType = @class != null && @class.IsValueType;
//if (isValueType)
//{
// WriteLine("auto this0 = (::{0}*) 0;", @class.QualifiedOriginalName);
//}
if (function.HasHiddenStructParameter)
{
Class retClass;
function.ReturnType.IsTagDecl(out retClass);
WriteLine("var {0} = new {1}.Internal();", GeneratedIdentifier("udt"),
retClass.OriginalName);
retType = new BuiltinType(PrimitiveType.Void);
needsReturn = false;
}
var @params = GenerateFunctionParamsMarshal(function.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);
}
var method = function as Method;
if (method != null && !method.IsStatic)
names.Insert(0, "Instance");
if (needsReturn)
Write("var ret = ");
WriteLine("Internal.{0}({1});", SafeIdentifier(function.OriginalName),
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.Visit(marshal);
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
Write(marshal.Context.SupportBefore);
WriteLine("return {0};", marshal.Context.Return);
}
if (function.HasHiddenStructParameter)
{
Class retClass;
function.ReturnType.IsTagDecl(out retClass);
WriteLine("var ret = new {0}();", retClass.Name);
if (isValueType)
throw new NotImplementedException();
else
WriteLine("*({0}.Internal*) ret.Instance.ToPointer() = {1};",
retClass.Name, GeneratedIdentifier("udt"));
WriteLine("return ret;");
}
}
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.Type
};
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};", argName, marshal.Context.Return);
}
return paramMarshal;
}
private void GenerateMethodParameters(Method method)
{
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 #endregion
@ -281,70 +986,55 @@ namespace Cxxi.Generators.CSharp
WriteCloseBraceIndent(); WriteCloseBraceIndent();
} }
public void GenerateFunction(Function function) public void GenerateFunction(Function function, Class @class = null)
{ {
if(function.Ignore) return; if(function.Ignore) return;
GenerateDeclarationCommon(function); GenerateDeclarationCommon(function);
Write("[DllImport(\"{0}.dll\", ", SafeIdentifier(Library.SharedLibrary)); WriteLine("[SuppressUnmanagedCodeSecurity]");
Write("CallingConvention = CallingConvention.{0}, ", ToCSharpCallConv(function.CallingConvention));
WriteLine("EntryPoint=\"{0}\")]", function.Mangled);
Write("public unsafe static extern {0} {1}(", function.ReturnType, Write("[DllImport(\"{0}.dll\", ", Library.SharedLibrary);
SafeIdentifier(function.Name)); WriteLine("CallingConvention = CallingConvention.{0}, ",
ToCSharpCallConv(function.CallingConvention));
WriteLineIndent("EntryPoint=\"{0}\")]", function.Mangled);
var @params = new List<string>(); if (function.ReturnType.Desugar().IsPrimitiveType(PrimitiveType.Bool))
for(var i = 0; i < function.Parameters.Count; ++i) WriteLine("[return: MarshalAsAttribute(UnmanagedType.I1)]");
{
var param = function.Parameters[i];
@params.Add(string.Format("{0} {1}", param.Type, SafeIdentifier(param.Name)));
}
Write(string.Join(", ", @params)); var @params = new List<string>();
WriteLine(");");
}
public static string ToCSharpCallConv(CallingConvention convention) var typePrinter = Type.TypePrinter as CSharpTypePrinter;
var retType = typePrinter.VisitParameterDecl(new Parameter()
{ {
switch (convention) QualifiedType = new QualifiedType() { Type = function.ReturnType }
});
var method = function as Method;
if (method != null)
{ {
case CallingConvention.Default: @params.Add("System.IntPtr instance");
return "Winapi";
case CallingConvention.C:
return "Cdecl";
case CallingConvention.StdCall:
return "StdCall";
case CallingConvention.ThisCall:
return "ThisCall";
case CallingConvention.FastCall:
return "FastCall";
}
return "Winapi"; if (method.IsConstructor && Options.IsMicrosoftAbi)
retType = "System.IntPtr";
} }
public void GenerateDebug(Declaration decl) for(var i = 0; i < function.Parameters.Count; ++i)
{ {
if(Options.OutputDebug && !String.IsNullOrWhiteSpace(decl.DebugText)) var param = function.Parameters[i];
WriteLine("// DEBUG: " + decl.DebugText); var typeName = param.Visit(typePrinter);
}
public void GenerateSummary(string comment) var paramName = param.IsSynthetized ?
{ GeneratedIdentifier(param.Name) : SafeIdentifier(param.Name);
if(String.IsNullOrWhiteSpace(comment))
return;
WriteLine("/// <summary>"); @params.Add(string.Format("{0} {1}", typeName, paramName));
WriteLine("/// {0}", comment);
WriteLine("/// </summary>");
} }
public void GenerateInlineSummary(string comment) if (method != null && method.IsConstructor)
{ if (Options.IsMicrosoftAbi && @class.Layout.HasVirtualBases)
if(String.IsNullOrWhiteSpace(comment)) @params.Add("int " + GeneratedIdentifier("forBases"));
return;
WriteLine("/// <summary>{0}</summary>", comment); WriteLine("public unsafe static extern {0} {1}({2});", retType,
SafeIdentifier(function.Name), string.Join(", ", @params));
} }
} }
} }

87
src/Generator/Generators/CSharp/CSharpTypePrinter.cs

@ -4,15 +4,55 @@ using Cxxi.Types;
namespace Cxxi.Generators.CSharp namespace Cxxi.Generators.CSharp
{ {
public enum CSharpTypePrinterContextKind
{
Native,
Managed
}
public class CSharpTypePrinterContext : TypePrinterContext
{
public CSharpTypePrinterContext()
{
}
public CSharpTypePrinterContext(TypePrinterContextKind kind)
: base(kind)
{
}
}
public class CSharpTypePrinter : ITypePrinter, IDeclVisitor<string> public class CSharpTypePrinter : ITypePrinter, IDeclVisitor<string>
{ {
public Library Library { get; set; } public Library Library { get; set; }
private readonly ITypeMapDatabase TypeMapDatabase; private readonly ITypeMapDatabase TypeMapDatabase;
private readonly Stack<CSharpTypePrinterContextKind> contexts;
public CSharpTypePrinterContextKind ContextKind
{
get { return contexts.Peek(); }
}
public CSharpTypePrinter(ITypeMapDatabase database, Library library) public CSharpTypePrinter(ITypeMapDatabase database, Library library)
{ {
TypeMapDatabase = database; TypeMapDatabase = database;
Library = library; Library = library;
contexts = new Stack<CSharpTypePrinterContextKind>();
PushContext(CSharpTypePrinterContextKind.Managed);
}
public void PushContext(CSharpTypePrinterContextKind contextKind)
{
contexts.Push(contextKind);
}
public CSharpTypePrinterContextKind PopContext()
{
return contexts.Pop();
} }
public string VisitTagType(TagType tag, TypeQualifiers quals) public string VisitTagType(TagType tag, TypeQualifiers quals)
@ -63,15 +103,24 @@ namespace Cxxi.Generators.CSharp
return string.Format("{0}", function.Visit(this, quals)); return string.Format("{0}", function.Visit(this, quals));
} }
var isManagedContext = ContextKind == CSharpTypePrinterContextKind.Managed;
if (pointee.IsPrimitiveType(PrimitiveType.Void, walkTypedefs: true) || if (pointee.IsPrimitiveType(PrimitiveType.Void, walkTypedefs: true) ||
pointee.IsPrimitiveType(PrimitiveType.UInt8, walkTypedefs: true)) pointee.IsPrimitiveType(PrimitiveType.UInt8, walkTypedefs: true))
{ {
return "IntPtr"; return isManagedContext ? "string" : "System.IntPtr";
} }
if (pointee.IsPrimitiveType(PrimitiveType.Char) && quals.IsConst) if (pointee.IsPrimitiveType(PrimitiveType.Char) && quals.IsConst)
{ {
return "string"; return isManagedContext ? "string" : "System.IntPtr";
}
Class @class;
if (pointee.IsTagDecl(out @class)
&& ContextKind == CSharpTypePrinterContextKind.Native)
{
return "System.IntPtr";
} }
return pointee.Visit(this, quals); return pointee.Visit(this, quals);
@ -95,7 +144,9 @@ namespace Cxxi.Generators.CSharp
TypeMap typeMap; TypeMap typeMap;
if (TypeMapDatabase.FindTypeMap(decl, out typeMap)) if (TypeMapDatabase.FindTypeMap(decl, out typeMap))
{ {
return typeMap.CSharpSignature(); typeMap.Type = typedef;
var ctx = new CSharpTypePrinterContext {Type = typedef};
return typeMap.CSharpSignature(ctx);
} }
FunctionType func; FunctionType func;
@ -108,12 +159,24 @@ namespace Cxxi.Generators.CSharp
return decl.Type.Visit(this); return decl.Type.Visit(this);
} }
public string VisitTemplateSpecializationType(TemplateSpecializationType template, TypeQualifiers quals) public string VisitTemplateSpecializationType(TemplateSpecializationType template,
TypeQualifiers quals)
{ {
throw new NotImplementedException(); var decl = template.Template.TemplatedDecl;
TypeMap typeMap = null;
if (TypeMapDatabase.FindTypeMap(template, out typeMap))
{
typeMap.Declaration = decl;
typeMap.Type = template;
return typeMap.CSharpSignature(new CSharpTypePrinterContext());
} }
public string VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals) return decl.Name;
}
public string VisitTemplateParameterType(TemplateParameterType param,
TypeQualifiers quals)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -135,6 +198,7 @@ namespace Cxxi.Generators.CSharp
case PrimitiveType.UInt64: return "ulong"; case PrimitiveType.UInt64: return "ulong";
case PrimitiveType.Float: return "float"; case PrimitiveType.Float: return "float";
case PrimitiveType.Double: return "double"; case PrimitiveType.Double: return "double";
case PrimitiveType.IntPtr: return "System.IntPtr";
} }
throw new NotSupportedException(); throw new NotSupportedException();
@ -173,7 +237,16 @@ namespace Cxxi.Generators.CSharp
public string VisitParameterDecl(Parameter parameter) public string VisitParameterDecl(Parameter parameter)
{ {
throw new NotImplementedException(); var paramType = parameter.Type;
Class @class;
if (paramType.Desugar().IsTagDecl(out @class)
&& ContextKind == CSharpTypePrinterContextKind.Native)
{
return string.Format("{0}.Internal", @class.Name);
}
return paramType.Visit(this);
} }
public string VisitTypedefDecl(TypedefDecl typedef) public string VisitTypedefDecl(TypedefDecl typedef)

3
src/Generator/Types/TypeMap.cs

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Cxxi.Generators; using Cxxi.Generators;
using Cxxi.Generators.CLI; using Cxxi.Generators.CLI;
using Cxxi.Generators.CSharp;
namespace Cxxi.Types namespace Cxxi.Types
{ {
@ -38,7 +39,7 @@ namespace Cxxi.Types
#region C# backend #region C# backend
public virtual string CSharpSignature() public virtual string CSharpSignature(CSharpTypePrinterContext ctx)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

Loading…
Cancel
Save