diff --git a/library/modules/std/objc_block.sx b/library/modules/std/objc_block.sx index 09d73cb..0d0f3cc 100644 --- a/library/modules/std/objc_block.sx +++ b/library/modules/std/objc_block.sx @@ -107,3 +107,70 @@ impl Into(Block) for Closure(bool) -> void { }; } } + +// Comptime builder for the generic `Into(Block) for Closure(..$args) +// -> $R` impl body. Receives the closure's per-position pack types +// (`args`, a comptime `[]Type`) and the closure's return type +// (`$ret`), emits source that: +// +// 1. Declares a nested `__invoke` `callconv(.c)` trampoline whose +// signature matches the per-shape Apple Block ABI: first arg is +// `block_self: *Block`, then the pack types verbatim. The body +// reconstructs a typed fn-pointer from `block_self.sx_fn`, +// prepends `block_self.sx_env`, and tail-calls the sx closure. +// 2. Returns a stack Block literal whose `invoke` slot points at +// the nested trampoline via `xx @__invoke`. +// +// The host impl wraps the emitted source via `#insert +// build_block_convert($args, $R);`. Per-call-shape monomorphisation +// of the impl body re-runs this builder with the concrete types +// bound, so each closure shape gets its own dedicated trampoline. +// +// Void return type emits `typed_fn(...)`; non-void emits +// `return typed_fn(...)`. +build_block_convert :: (args: []Type, $ret: Type) -> string { + ret_name := type_name(ret); + code := "__invoke :: (block_self: *Block"; + i : s64 = 0; + while i < args.len { + code = concat(code, ", arg"); + code = concat(code, int_to_string(i)); + code = concat(code, ": "); + code = concat(code, type_name(args[i])); + i = i + 1; + } + code = concat(code, ") -> "); + code = concat(code, ret_name); + code = concat(code, " callconv(.c) { typed_fn : (*void"); + i = 0; + while i < args.len { + code = concat(code, ", "); + code = concat(code, type_name(args[i])); + i = i + 1; + } + code = concat(code, ") -> "); + code = concat(code, ret_name); + code = concat(code, " = xx block_self.sx_fn; "); + if ret_name == "void" { + code = concat(code, "typed_fn(block_self.sx_env"); + } else { + code = concat(code, "return typed_fn(block_self.sx_env"); + } + i = 0; + while i < args.len { + code = concat(code, ", arg"); + code = concat(code, int_to_string(i)); + i = i + 1; + } + code = concat(code, "); } "); + code = concat(code, "return .{ "); + code = concat(code, "isa = @_NSConcreteStackBlock, "); + code = concat(code, "flags = 0, "); + code = concat(code, "reserved = 0, "); + code = concat(code, "invoke = xx @__invoke, "); + code = concat(code, "descriptor = xx @__sx_block_descriptor, "); + code = concat(code, "sx_env = self.env, "); + code = concat(code, "sx_fn = self.fn_ptr, "); + code = concat(code, "};"); + return code; +}