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.
 
 
 
 
 

561 lines
18 KiB

using System.Text;
using CppSharp.Types;
namespace CppSharp.Generators.CSharp
{
public enum CSharpMarshalKind
{
Unknown,
NativeField
}
public class CSharpMarshalContext : MarshalContext
{
public CSharpMarshalContext(Driver driver)
: base(driver)
{
Kind = CSharpMarshalKind.Unknown;
Cleanup = new TextGenerator();
}
public CSharpMarshalKind Kind { get; set; }
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.Desugar().IsPrimitiveType(PrimitiveType.Void))
{
Context.Return.Write("new System.IntPtr({0})", Context.ReturnVarName);
return true;
}
if (CSharpTypePrinter.IsConstCharString(pointer))
{
Context.Return.Write("Marshal.PtrToStringAnsi(new System.IntPtr({0}))",
Context.ReturnVarName);
return true;
}
PrimitiveType primitive;
if (pointee.Desugar().IsPrimitiveType(out primitive))
{
Context.Return.Write("new System.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.CSharpMarshalToManaged(Context);
return typeMap.IsValueType;
}
FunctionType function;
if (decl.Type.IsPointerTo(out function))
{
Context.SupportBefore.WriteLine("var {0} = new System.IntPtr({1});",
Helpers.GeneratedIdentifier("ptr"), Context.ReturnVarName);
Context.Return.Write("({1})Marshal.GetDelegateForFunctionPointer({0}, typeof({1}))",
Helpers.GeneratedIdentifier("ptr"), typedef.ToString());
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 ctx = Context as CSharpMarshalContext;
string instance = Context.ReturnVarName;
if (ctx.Kind == CSharpMarshalKind.NativeField)
{
if (Context.ReturnVarName.StartsWith("*"))
Context.ReturnVarName = Context.ReturnVarName.Substring(1);
instance = string.Format("new System.IntPtr({0})",
Context.ReturnVarName);
}
Context.Return.Write("new {0}({1})", QualifiedIdentifier(@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 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}", Context.ReturnVarName);
return true;
}
public override bool VisitVariableDecl(Variable variable)
{
return variable.Type.Visit(this, variable.QualifiedType.Qualifiers);
}
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("({0})(", type);
//sb.Append("Marshal.GetFunctionPointerForDelegate(");
//sb.AppendFormat("{0}).ToPointer())", Context.Parameter.Name);
//Context.Return.Write(sb.ToString());
Context.Return.Write(Context.Parameter.Name);
return true;
}
public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals)
{
var pointee = pointer.Pointee;
var isVoidPtr = pointee.Desugar().IsPrimitiveType(PrimitiveType.Void);
var isUInt8Ptr = pointee.Desugar().IsPrimitiveType(PrimitiveType.UInt8);
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",
Helpers.SafeIdentifier(Context.Parameter.Name));
else
Context.Return.Write("new System.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.Desugar().IsPrimitiveType(out primitive))
{
//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("{0}", Helpers.InstanceIdentifier);
return;
}
if (Context.Parameter.Type.IsPointer())
Context.Return.Write("{0}.{1}", Context.Parameter.Name,
Helpers.InstanceIdentifier);
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 CSharpMarshalContext(Context.Driver)
{
ArgName = fieldRef,
ParameterIndex = Context.ParameterIndex++
};
var marshal = new CSharpMarshalManagedToNativePrinter(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
{
if (parameter.Kind == ParameterKind.OperatorParameter)
{
Context.Return.Write("new System.IntPtr(&{0})",
parameter.Name);
return true;
}
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(Context.Parameter.Name);
return true;
}
public override bool VisitClassTemplateDecl(ClassTemplate template)
{
return template.TemplatedClass.Visit(this);
}
}
}