internal static unsafe class {{ callback.bridge_name }}
{
private static readonly global::System.Collections.Concurrent.ConcurrentDictionary<ulong, {{ callback.public_name }}> Handles = new();
private static long _nextHandle = -1;
private static readonly IntPtr VTablePtr;
static {{ callback.bridge_name }}()
{
VTable table = new VTable
{
free = Marshal.GetFunctionPointerForDelegate(FreeDelegate),
clone = Marshal.GetFunctionPointerForDelegate(CloneDelegate){% for method in callback.methods %},
{{ method.vtable_field }} = Marshal.GetFunctionPointerForDelegate({{ method.name }}Delegate){% endfor %}
};
VTablePtr = (IntPtr)NativeMemory.Alloc((nuint)Marshal.SizeOf<VTable>());
Marshal.StructureToPtr(table, VTablePtr, false);
RegisterNative(VTablePtr);
}
internal static BoltFFICallbackHandle Create({{ callback.public_name }}? impl_)
{
_ = VTablePtr;
if (impl_ is null) return BoltFFICallbackHandle.Null;
if (impl_ is {{ callback.proxy_name }} proxy) return proxy.CloneHandle();
return CreateNative(Insert(impl_));
}
internal static {{ callback.proxy_name }} Wrap(BoltFFICallbackHandle handle)
{
if (handle.IsNull) throw new InvalidOperationException("callback handle is null");
return new {{ callback.proxy_name }}(handle);
}
private static ulong Insert({{ callback.public_name }} impl_)
{
ulong handle = unchecked((ulong)global::System.Threading.Interlocked.Add(ref _nextHandle, 2));
Handles[handle] = impl_;
return handle;
}
private static void Free(ulong handle) => Handles.TryRemove(handle, out _);
private static ulong Clone(ulong handle)
{
return Handles.TryGetValue(handle, out {{ callback.public_name }}? impl_) ? Insert(impl_) : 0;
}
internal static void ThrowIfCallbackStatus(FfiStatus status)
{
if (status.code != 0) throw new InvalidOperationException($"callback failed with status code {status.code}");
}
{% for method in callback.methods %}
{%- match method.entry %}
{%- when CSharpCallbackEntryPlan::Sync with (entry) %}
private static void {{ method.name }}({{ entry.native_params }})
{
{%- match entry.out_initializer %}
{%- when CSharpSyncCallbackOutInitializerPlan::Void %}
{%- when CSharpSyncCallbackOutInitializerPlan::Direct with { default_value } %}
__boltffiOutPtr = {{ default_value }};
{%- when CSharpSyncCallbackOutInitializerPlan::Encoded %}
__boltffiOutPtr = IntPtr.Zero;
__boltffiOutLen = UIntPtr.Zero;
{%- endmatch %}
__boltffiStatus = new FfiStatus { code = 100 };
try
{
if (!Handles.TryGetValue(handle, out var impl_)) throw new InvalidOperationException("invalid callback handle");
{%- for param in entry.bridge_params %}
{%- match param %}
{%- when CSharpCallbackBridgeParamPlan::Direct with { public_param, native_param, decoded_arg, proxy_arg } %}
{%- when CSharpCallbackBridgeParamPlan::WireEncoded with { public_param, native_ptr_param, native_len_param, reader_local, decoded_arg, writer, pin_local, ptr_local } %}
var {{ reader_local }} = new WireReader({{ native_ptr_param.name }}, {{ native_len_param.name }});
{%- endmatch %}
{%- endfor %}
{%- match entry.success %}
{%- when CSharpSyncCallbackSuccessPlan::Void with { decoded_args } %}
impl_.{{ method.name }}({{ decoded_args }});
__boltffiStatus = new FfiStatus { code = 0 };
{%- when CSharpSyncCallbackSuccessPlan::Direct with { decoded_args, native_value_expr } %}
var __boltffiValue = impl_.{{ method.name }}({{ decoded_args }});
__boltffiOutPtr = {{ native_value_expr }};
__boltffiStatus = new FfiStatus { code = 0 };
{%- when CSharpSyncCallbackSuccessPlan::Encoded with { is_result, decoded_args, result_assignment, writer } %}
{%- if let Some(result_assignment) = result_assignment %}
BoltFFIResult<{{ result_assignment.result_type.ok_type }}, {{ result_assignment.result_type.err_type }}> __boltffiResult;
try
{
{%- match result_assignment.ok %}
{%- when CSharpCallbackResultOkPlan::Void with { receiver, method_name, args } %}
{{ receiver }}.{{ method_name }}({{ args }});
__boltffiResult = BoltFFIResult<{{ result_assignment.result_type.ok_type }}, {{ result_assignment.result_type.err_type }}>.Ok(default);
{%- when CSharpCallbackResultOkPlan::Value with { receiver, method_name, args } %}
__boltffiResult = BoltFFIResult<{{ result_assignment.result_type.ok_type }}, {{ result_assignment.result_type.err_type }}>.Ok({{ receiver }}.{{ method_name }}({{ args }}));
{%- endmatch %}
}
{%- match result_assignment.catch %}
{%- when Some with (catch_plan) %}
{%- match catch_plan %}
{%- when CSharpCallbackResultCatchPlan::TypedException with { exception_type } %}
catch ({{ exception_type }} __boltffiException)
{
__boltffiResult = BoltFFIResult<{{ result_assignment.result_type.ok_type }}, {{ result_assignment.result_type.err_type }}>.Err(__boltffiException.Error);
}
{%- when CSharpCallbackResultCatchPlan::ExceptionMessage %}
catch (Exception __boltffiException)
{
__boltffiResult = BoltFFIResult<{{ result_assignment.result_type.ok_type }}, {{ result_assignment.result_type.err_type }}>.Err(__boltffiException.Message);
}
{%- endmatch %}
{%- when None %}
catch
{
throw;
}
{%- endmatch %}
{%- else %}
var __boltffiValue = impl_.{{ method.name }}({{ decoded_args }});
{%- endif %}
byte[] {{ writer.bytes_binding_name }};
using (var {{ writer.binding_name }} = new WireWriter({{ writer.size_expr }}))
{
{%- for stmt in writer.encode_stmts %}
{{ stmt }};
{%- endfor %}
{{ writer.bytes_binding_name }} = {{ writer.binding_name }}.ToArray();
}
BoltFFICallbackReturn.Store({{ writer.bytes_binding_name }}, out __boltffiOutPtr, out __boltffiOutLen);
__boltffiStatus = new FfiStatus { code = 0 };
{%- endmatch %}
}
catch
{
{%- match entry.out_initializer %}
{%- when CSharpSyncCallbackOutInitializerPlan::Void %}
{%- when CSharpSyncCallbackOutInitializerPlan::Direct with { default_value } %}
__boltffiOutPtr = {{ default_value }};
{%- when CSharpSyncCallbackOutInitializerPlan::Encoded %}
__boltffiOutPtr = IntPtr.Zero;
__boltffiOutLen = UIntPtr.Zero;
{%- endmatch %}
__boltffiStatus = new FfiStatus { code = 100 };
}
}
{%- when CSharpCallbackEntryPlan::Async with (entry) %}
private static void {{ method.name }}({{ entry.native_params }})
{
if (!Handles.TryGetValue(handle, out var impl_))
{
{%- let completion = entry.invalid_handle_completion %}
{%- let completion_indent = " " %}
{%- include "render_csharp/callback_async_failure_completion.txt" %}
return;
}
try
{
{%- for param in entry.bridge_params %}
{%- match param %}
{%- when CSharpCallbackBridgeParamPlan::Direct with { public_param, native_param, decoded_arg, proxy_arg } %}
{%- when CSharpCallbackBridgeParamPlan::WireEncoded with { public_param, native_ptr_param, native_len_param, reader_local, decoded_arg, writer, pin_local, ptr_local } %}
var {{ reader_local }} = new WireReader({{ native_ptr_param.name }}, {{ native_len_param.name }});
{%- endmatch %}
{%- endfor %}
var __boltffiTask = impl_.{{ method.name }}({{ entry.decoded_args }});
_ = __boltffiTask.ContinueWith(__boltffiCompleted =>
{
if (__boltffiCompleted.IsCanceled)
{
{%- let completion = entry.canceled_completion %}
{%- let completion_indent = " " %}
{%- include "render_csharp/callback_async_failure_completion.txt" %}
return;
}
if (__boltffiCompleted.IsFaulted)
{
Exception __boltffiException = UnwrapTaskException(__boltffiCompleted.Exception!);
{%- match entry.faulted_completion %}
{%- when CSharpAsyncCallbackFaultPlan::Failure with (failure) %}
{%- let completion = failure %}
{%- let completion_indent = " " %}
{%- include "render_csharp/callback_async_failure_completion.txt" %}
{%- when CSharpAsyncCallbackFaultPlan::EncodedResult with { exception_type, error_value_expr, result_type, writer, fallback } %}
{%- if let Some(exception_type) = exception_type %}
if (__boltffiException is {{ exception_type }} __boltffiTypedException)
{
{%- else %}
{
{%- endif %}
var __boltffiErrorResult = BoltFFIResult<{{ result_type.ok_type }}, {{ result_type.err_type }}>.Err({{ error_value_expr }});
byte[] {{ writer.bytes_binding_name }};
using (var {{ writer.binding_name }} = new WireWriter({{ writer.size_expr }}))
{
{%- for stmt in writer.encode_stmts %}
{{ stmt }};
{%- endfor %}
{{ writer.bytes_binding_name }} = {{ writer.binding_name }}.ToArray();
}
BoltFFICallbackReturn.Store({{ writer.bytes_binding_name }}, out var __boltffiErrorOutPtr, out var __boltffiErrorOutLen);
try
{
callback(callbackData, __boltffiErrorOutPtr, __boltffiErrorOutLen, new FfiStatus { code = 0 });
}
finally
{
BoltFFICallbackReturn.Free(__boltffiErrorOutPtr);
}
return;
}
{%- if let Some(fallback) = fallback %}
{%- let completion = fallback %}
{%- let completion_indent = " " %}
{%- include "render_csharp/callback_async_failure_completion.txt" %}
{%- endif %}
{%- endmatch %}
return;
}
{%- match entry.success_completion %}
{%- when CSharpAsyncCallbackSuccessPlan::Void %}
callback(callbackData, new FfiStatus { code = 0 });
{%- when CSharpAsyncCallbackSuccessPlan::Direct with { native_value_expr } %}
callback(callbackData, {{ native_value_expr }}, new FfiStatus { code = 0 });
{%- when CSharpAsyncCallbackSuccessPlan::Encoded with { is_result, result_type, writer } %}
{%- if is_result %}
var __boltffiResult = BoltFFIResult<{{ result_type.ok_type }}, {{ result_type.err_type }}>.Ok(__boltffiCompleted.Result);
{%- else %}
var __boltffiValue = __boltffiCompleted.Result;
{%- endif %}
byte[] {{ writer.bytes_binding_name }};
using (var {{ writer.binding_name }} = new WireWriter({{ writer.size_expr }}))
{
{%- for stmt in writer.encode_stmts %}
{{ stmt }};
{%- endfor %}
{{ writer.bytes_binding_name }} = {{ writer.binding_name }}.ToArray();
}
BoltFFICallbackReturn.Store({{ writer.bytes_binding_name }}, out var __boltffiOutPtr, out var __boltffiOutLen);
try
{
callback(callbackData, __boltffiOutPtr, __boltffiOutLen, new FfiStatus { code = 0 });
}
finally
{
BoltFFICallbackReturn.Free(__boltffiOutPtr);
}
{%- endmatch %}
}, global::System.Threading.Tasks.TaskScheduler.Default);
}
catch
{
{%- let completion = entry.catch_completion %}
{%- let completion_indent = " " %}
{%- include "render_csharp/callback_async_failure_completion.txt" %}
}
}
{%- endmatch %}
{% endfor %}
{% if callback.has_async_methods %}
private static Exception UnwrapTaskException(Exception exception)
{
while (exception is AggregateException aggregate && aggregate.InnerException is { } inner)
{
exception = inner;
}
return exception;
}
{% endif %}
[StructLayout(LayoutKind.Sequential)]
internal struct VTable
{
public IntPtr free;
public IntPtr clone;
{% for method in callback.methods %}
public IntPtr {{ method.vtable_field }};
{% endfor %}
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void FreeFn(ulong handle);
private static readonly FreeFn FreeDelegate = Free;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate ulong CloneFn(ulong handle);
private static readonly CloneFn CloneDelegate = Clone;
{% for method in callback.methods %}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void {{ method.name }}Fn({{ method.delegates.entry_params }});
private static readonly {{ method.name }}Fn {{ method.name }}Delegate = {{ method.name }};
{%- if let Some(completion_params) = method.delegates.completion_params %}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void {{ method.name }}Completion({{ completion_params }});
{%- endif %}
{%- if let Some(proxy_params) = method.delegates.proxy_params %}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void {{ method.name }}ProxyFn({{ proxy_params }});
{%- endif %}
{% endfor %}
[DllImport(NativeMethods.LibName, EntryPoint = "{{ callback.register_fn }}")]
private static extern void RegisterNative(IntPtr vtable);
[DllImport(NativeMethods.LibName, EntryPoint = "{{ callback.create_fn }}")]
private static extern BoltFFICallbackHandle CreateNative(ulong handle);
}