[runtime] Support automatic GCHandle -> MonoObject* conversion for parameters and return values when calling managed delegates.
This commit is contained in:
Родитель
95a9fe33fd
Коммит
1c33bff5b3
|
@ -26,7 +26,7 @@ namespace ObjCRuntime {
|
|||
public unsafe partial class Runtime {
|
||||
|
||||
<# foreach (var d in delegates) { #>
|
||||
internal delegate <#= d.MReturnType #> <#= d.SimpleEntryPoint #>_delegate (<#= d.MArgumentSignature #>);
|
||||
internal delegate <#= d.ReturnType.MType #> <#= d.SimpleEntryPoint #>_delegate (<#= d.MArgumentSignature #>);
|
||||
<# } #>
|
||||
|
||||
internal struct Delegates {
|
||||
|
@ -37,7 +37,7 @@ namespace ObjCRuntime {
|
|||
|
||||
<# foreach (var d in delegates) { #>
|
||||
[MonoPInvokeCallback (typeof (<#= d.SimpleEntryPoint #>_delegate))]
|
||||
static <#= d.MReturnType #> <#= d.SimpleEntryPoint #> (<#= d.MArgumentSignature #>)
|
||||
static <#= d.ReturnType.MType #> <#= d.SimpleEntryPoint #> (<#= d.MArgumentSignature #>)
|
||||
<# if (d.ExceptionHandling) { #>
|
||||
{
|
||||
exception_gchandle = 0;
|
||||
|
@ -45,14 +45,14 @@ namespace ObjCRuntime {
|
|||
<# if (string.IsNullOrEmpty (d.WrappedManagedFunction)) { #>
|
||||
throw new NotImplementedException ();
|
||||
<# } else { #>
|
||||
<# if (d.MReturnType != "void") { #>return <# } #><#=d.WrappedManagedFunction#> (<#=d.MArgumentNames#>);
|
||||
<# if (d.ReturnType.MType != "void") { #>return <# } #><#=d.WrappedManagedFunction#> (<#=d.MArgumentNames#>);
|
||||
<# } #>
|
||||
} catch (Exception ex) {
|
||||
var handle = GCHandle.Alloc (ex, GCHandleType.Normal);
|
||||
exception_gchandle = GCHandle.ToIntPtr (handle).ToInt32 ();
|
||||
<# if (d.SimpleEntryPoint == "get_nsobject_with_type") { #> created = false;
|
||||
<# } #>
|
||||
<# if (d.MReturnType != "void") { #> return default (<#= d.MReturnType #>);
|
||||
<# if (d.ReturnType.MType != "void") { #> return default (<#= d.ReturnType.MType #>);
|
||||
<# } #>
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ namespace ObjCRuntime {
|
|||
<# if (string.IsNullOrEmpty (d.WrappedManagedFunction)) { #>
|
||||
throw new NotImplementedException ();
|
||||
<# } else { #>
|
||||
<# if (d.MReturnType != "void") { #>return <# } #><#=d.WrappedManagedFunction#> (<#=d.MArgumentNames#>);
|
||||
<# if (d.ReturnType.MType != "void") { #>return <# } #><#=d.WrappedManagedFunction#> (<#=d.MArgumentNames#>);
|
||||
<# } #>
|
||||
}
|
||||
<# } #>
|
||||
|
|
|
@ -22,8 +22,8 @@ extern "C" {
|
|||
if (!d.OnlyDynamicUsage)
|
||||
continue;
|
||||
#>
|
||||
<#= d.CReturnType #>
|
||||
<#= d.EntryPoint #> (<#= d.CArgumentSignature #>);
|
||||
<#= d.ReturnType.ExposedCType #>
|
||||
<#= d.EntryPoint #> (<#= d.CArgumentSignatureExposed #>);
|
||||
|
||||
<# } #>
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "delegates.h"
|
||||
|
||||
<# foreach (var d in delegates) { #>
|
||||
typedef <#= d.CReturnType #><#= d.AlignCReturnType #> (*func_<#= d.EntryPoint #>)<#= d.AlignEntryPoint #> (<#= d.CArgumentSignature #>);
|
||||
typedef <#= d.ReturnType.InterfaceCType #><#= d.AlignCReturnType #> (*func_<#= d.EntryPoint #>)<#= d.AlignEntryPoint #> (<#= d.CArgumentSignature #>);
|
||||
<# } #>
|
||||
|
||||
struct Delegates {
|
||||
|
@ -37,21 +37,5 @@ create_linked_away_exception (const char *func)
|
|||
}
|
||||
|
||||
<# foreach (var d in delegates) { #>
|
||||
<#= d.CReturnType #>
|
||||
<#= d.EntryPoint #> (<#= d.CArgumentSignature #>)
|
||||
{
|
||||
<#if (d.ExceptionHandling && d.OnlyDynamicUsage) {#> if (delegates.<#= d.EntryPoint.Substring ("xamarin_".Length) #> == NULL) {
|
||||
*exception_gchandle = create_linked_away_exception ("<#= d.EntryPoint.Substring ("xamarin_".Length) #>");
|
||||
return<# if (d.CReturnType != "void") { #> (<#= d.CReturnType #>) 0<# } #>;
|
||||
}
|
||||
<#} else {#>#if DEBUG
|
||||
if (delegates.<#= d.EntryPoint.Substring ("xamarin_".Length) #> == NULL) {
|
||||
NSLog (@PRODUCT ": The managed function <#= d.EntryPoint.Substring ("xamarin_".Length) #> could not be loaded.");
|
||||
xamarin_assertion_message ("The managed function <#= d.EntryPoint.Substring ("xamarin_".Length) #> could not be loaded.");
|
||||
}
|
||||
#endif
|
||||
<#}#>
|
||||
<# if (d.CReturnType != "void") { #>return <# } #>delegates.<#= d.EntryPoint.Substring ("xamarin_".Length) #> (<#= d.CArgumentNames #>);
|
||||
}
|
||||
|
||||
<#= d.Function #>
|
||||
<# } #>
|
||||
|
|
|
@ -279,9 +279,24 @@
|
|||
#><#+
|
||||
class Arg
|
||||
{
|
||||
public string CType;
|
||||
public string MangledCType;
|
||||
public string ExposedCType; // the CType exposed to native code
|
||||
public string InterfaceCType; // the CType as interfaced with managed code
|
||||
public string MType;
|
||||
public string Name;
|
||||
public bool IsGCHandleConversion => MangledCType.Contains ("->");
|
||||
public Arg (string mangledCType, string mType, string name)
|
||||
{
|
||||
MangledCType = mangledCType;
|
||||
var is_conv = IsGCHandleConversion;
|
||||
var interfaced = is_conv ? "GCHandle" : mangledCType;
|
||||
var exposed = is_conv ? XDelegate.GetConvertedGCHandleType (mangledCType) : mangledCType;
|
||||
ExposedCType = exposed;
|
||||
InterfaceCType = interfaced;
|
||||
MType = mType;
|
||||
Name = name;
|
||||
}
|
||||
public bool IsVoid => ExposedCType == "void";
|
||||
}
|
||||
|
||||
class XDelegates : List<XDelegate>
|
||||
|
@ -290,7 +305,7 @@
|
|||
{
|
||||
foreach (var x in this) {
|
||||
MaxEntryPointLength = Math.Max (MaxEntryPointLength, x.EntryPoint.Length);
|
||||
MaxCReturnTypeLength = Math.Max (MaxCReturnTypeLength, x.CReturnType.Length);
|
||||
MaxCReturnTypeLength = Math.Max (MaxCReturnTypeLength, x.ReturnType.ExposedCType.Length);
|
||||
x.Delegates = this;
|
||||
}
|
||||
}
|
||||
|
@ -301,8 +316,7 @@
|
|||
|
||||
class XDelegate
|
||||
{
|
||||
public string CReturnType;
|
||||
public string MReturnType;
|
||||
public Arg ReturnType;
|
||||
public string EntryPoint;
|
||||
public List<Arg> Arguments;
|
||||
public string WrappedManagedFunction;
|
||||
|
@ -310,12 +324,22 @@
|
|||
// Detemines whether the function is only used by the dynamic registrar (in which case we might be able to link the function away if the static registrar is being used)
|
||||
public bool OnlyDynamicUsage;
|
||||
|
||||
public string DelegateName {
|
||||
get {
|
||||
return EntryPoint.Substring ("xamarin_".Length);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetConvertedGCHandleType (string type)
|
||||
{
|
||||
return type.Substring ("GCHandle->".Length);
|
||||
}
|
||||
|
||||
public XDelegates Delegates;
|
||||
|
||||
public XDelegate (string cReturnType, string mReturnType, string entryPoint, params string [] arguments)
|
||||
{
|
||||
CReturnType = cReturnType;
|
||||
MReturnType = mReturnType;
|
||||
ReturnType = new Arg (cReturnType, mReturnType, string.Empty);
|
||||
EntryPoint = entryPoint;
|
||||
|
||||
if (arguments == null || arguments.Length == 0)
|
||||
|
@ -329,11 +353,86 @@
|
|||
|
||||
Arguments = new List<Arg> ();
|
||||
for (var i = 0; i < arguments.Length; i += 3)
|
||||
Arguments.Add (new Arg {
|
||||
CType = arguments [i],
|
||||
MType = arguments [i + 1],
|
||||
Name = arguments [i + 2]
|
||||
});
|
||||
Arguments.Add (new Arg (arguments [i], arguments [i + 1], arguments [i + 2]));
|
||||
}
|
||||
|
||||
public string Function {
|
||||
get {
|
||||
var sb = new StringBuilder ();
|
||||
|
||||
// This function generates the helper function that actually calls the managed delegate
|
||||
// It supports converting input arguments of MonoObject* (and equivalent types) to GCHandle,
|
||||
// and converting GCHandle return values to MonoObject* (or equivalent types). In both cases
|
||||
// the GCHandle will be freed before the generated function returns.
|
||||
|
||||
sb.AppendLine (ReturnType.ExposedCType);
|
||||
|
||||
sb.Append (EntryPoint);
|
||||
sb.Append (" (");
|
||||
sb.Append (CArgumentSignatureExposed);
|
||||
sb.AppendLine (")");
|
||||
sb.AppendLine ("{");
|
||||
|
||||
if (ExceptionHandling && OnlyDynamicUsage) {
|
||||
sb.AppendLine ($"\tif (delegates.{DelegateName} == NULL) {{");
|
||||
sb.AppendLine ($"\t\t*exception_gchandle = create_linked_away_exception (\"{DelegateName}\");");
|
||||
sb.AppendLine ($"\t\treturn{(ReturnType.IsVoid ? string.Empty : $" ({ReturnType.ExposedCType}) 0")};");
|
||||
sb.AppendLine ($"\t}}");
|
||||
} else {
|
||||
sb.AppendLine ($"#if DEBUG");
|
||||
sb.AppendLine ($"\tif (delegates.{DelegateName} == NULL) {{");
|
||||
sb.AppendLine ($"\t\tNSLog (@PRODUCT \": The managed function {DelegateName} could not be loaded.\");");
|
||||
sb.AppendLine ($"\t\txamarin_assertion_message (\"The managed function {DelegateName} could not be loaded.\");");
|
||||
sb.AppendLine ($"\t}}");
|
||||
sb.AppendLine ($"#endif");
|
||||
}
|
||||
|
||||
var invoke_args = new StringBuilder ();
|
||||
var post_invoke = new StringBuilder ();
|
||||
for (var i = 0; i < Arguments.Count; i++) {
|
||||
var arg = Arguments [i];
|
||||
if (i > 0)
|
||||
invoke_args.Append (", ");
|
||||
if (arg.IsGCHandleConversion) {
|
||||
// Convert to GCHandle before calling the managed functino
|
||||
var argname = $"{arg.Name}__handle";
|
||||
sb.AppendLine ($"\tGCHandle {argname} = xamarin_gchandle_new ((MonoObject *) {arg.Name}, false);");
|
||||
invoke_args.Append (argname);
|
||||
// and free the GCHandle after returning from the managed function
|
||||
post_invoke.AppendLine ($"\txamarin_gchandle_free ({argname});");
|
||||
} else {
|
||||
invoke_args.Append (arg.Name);
|
||||
}
|
||||
}
|
||||
|
||||
sb.Append ("\t");
|
||||
if (!ReturnType.IsVoid) {
|
||||
sb.Append ($"{ReturnType.ExposedCType} rv = ");
|
||||
// Unwrap the GCHandle and free it
|
||||
if (ReturnType.IsGCHandleConversion)
|
||||
sb.Append ("xamarin_gchandle_unwrap (");
|
||||
}
|
||||
|
||||
sb.Append ("delegates.");
|
||||
sb.Append (EntryPoint.Substring ("xamarin_".Length));
|
||||
sb.Append (" (");
|
||||
sb.Append (invoke_args);
|
||||
if (ExceptionHandling)
|
||||
sb.Append (", exception_gchandle");
|
||||
|
||||
if (ReturnType.IsGCHandleConversion)
|
||||
sb.Append (")");
|
||||
|
||||
sb.AppendLine (");");
|
||||
|
||||
sb.Append (post_invoke);
|
||||
|
||||
if (!ReturnType.IsVoid)
|
||||
sb.AppendLine ("\treturn rv;");
|
||||
|
||||
sb.AppendLine ("}");
|
||||
return sb.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
public string SimpleEntryPoint {
|
||||
|
@ -350,11 +449,11 @@
|
|||
|
||||
public string AlignCReturnType {
|
||||
get {
|
||||
return new string (' ', Delegates.MaxCReturnTypeLength - CReturnType.Length);
|
||||
return new string (' ', Delegates.MaxCReturnTypeLength - ReturnType.ExposedCType.Length);
|
||||
}
|
||||
}
|
||||
|
||||
string CFormatArgs (string empty, bool nameOnly)
|
||||
string CFormatArgs (string empty, bool nameOnly, bool exposed = false)
|
||||
{
|
||||
if (Arguments == null || Arguments.Count == 0)
|
||||
return empty;
|
||||
|
@ -363,7 +462,7 @@
|
|||
|
||||
foreach (var arg in Arguments) {
|
||||
if (!nameOnly) {
|
||||
builder.Append (arg.CType);
|
||||
builder.Append (exposed ? arg.ExposedCType : arg.InterfaceCType);
|
||||
builder.Append (' ');
|
||||
}
|
||||
|
||||
|
@ -422,6 +521,10 @@
|
|||
get { return CFormatArgs ("void", nameOnly: false); }
|
||||
}
|
||||
|
||||
public string CArgumentSignatureExposed {
|
||||
get { return CFormatArgs ("void", nameOnly: false, exposed: true); }
|
||||
}
|
||||
|
||||
public string CArgumentNames {
|
||||
get { return CFormatArgs (String.Empty, nameOnly: true); }
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ extern "C" {
|
|||
if (d.OnlyDynamicUsage)
|
||||
continue;
|
||||
#>
|
||||
<#= d.CReturnType #>
|
||||
<#= d.EntryPoint #> (<#= d.CArgumentSignature #>);
|
||||
<#= d.ReturnType.ExposedCType #>
|
||||
<#= d.EntryPoint #> (<#= d.CArgumentSignatureExposed #>);
|
||||
|
||||
<# } #>
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче