boltffi_bindgen 0.25.0

Code generation library for BoltFFI - generates Swift, Kotlin, and TypeScript bindings
Documentation
    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);
    }