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.
561 lines
18 KiB
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); |
|
} |
|
} |
|
}
|
|
|