ffi 0.6: C-to-sx callback baseline (1-arg + ctx-ptr forms)

93/93 regression tests pass (+ffi-06-callback).

Mirrors the `app->onInputEvent` install pattern from
library/modules/platform/android.sx:

  1. (s32) -> s32              — single primitive arg/return
  2. (*void, s32) -> s32       — opaque ctx pointer + value
                                  (the onInputEvent shape)

Side effects via two file-level globals so the test observes both
the return value AND state mutation across multiple calls:
- g_callback_hits = N proves the callback fired N times.
- g_callback_sum  = sum of args proves each individual call landed
  with the correct value.

The ctx-pointer variant casts `*void` back to `*s32` inside the
callback and reads through it (`p.*`), proving the pointer survives
the round-trip with no aliasing weirdness.
This commit is contained in:
agra
2026-05-19 11:48:34 +03:00
parent 31715bd251
commit 31ab175d56
5 changed files with 89 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
// Phase 0 baseline (PLAN-FFI.md step 0.6): sx function passed to C
// as a function pointer; C invokes it; sx-side observable effect.
// Mirrors the `app->onInputEvent` install pattern in
// library/modules/platform/android.sx.
//
// Two arities covered:
// 1. (s32) -> s32 — single-arg callback
// 2. (*void, s32) -> s32 — pointer + value (onInputEvent shape)
//
// Plus a side-effect via a global so we can confirm the callback
// actually fired (return value + state mutation both observable).
#import "modules/std.sx";
#import c {
#source "vendors/ffi_callback/ffi_callback.c";
};
ffi_apply_callback :: (cb: (s32) -> s32, value: s32) -> s32 #foreign;
ffi_apply_callback2 :: (cb: (*void, s32) -> s32, ctx: *void, v: s32) -> s32 #foreign;
g_callback_hits : s32 = 0;
g_callback_sum : s32 = 0;
double_it :: (x: s32) -> s32 {
g_callback_hits += 1;
g_callback_sum += x;
x * 2;
}
add_with_ctx :: (ctx: *void, v: s32) -> s32 {
g_callback_hits += 1;
// Pass a sentinel via ctx to prove the pointer arg also survives the
// round-trip — read it back as an s32 through *s32.
p : *s32 = xx ctx;
p.* + v;
}
main :: () -> s32 {
// ── Single-arg callback ────────────────────────────────────────
r1 := ffi_apply_callback(double_it, 21);
print("callback returned = {}\n", r1);
print("hits after first call = {}\n", g_callback_hits);
print("sum after first call = {}\n", g_callback_sum);
// Two more calls confirm the same fn pointer keeps working.
ffi_apply_callback(double_it, 7);
ffi_apply_callback(double_it, 11);
print("hits after three calls = {}\n", g_callback_hits);
print("sum after three calls = {}\n", g_callback_sum);
// ── Two-arg callback with opaque ctx pointer ───────────────────
ctx_val : s32 = 100;
r2 := ffi_apply_callback2(add_with_ctx, xx @ctx_val, 42);
print("ctx + value = {}\n", r2);
print("hits after ctx callback = {}\n", g_callback_hits);
0;
}

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1,7 @@
callback returned = 42
hits after first call = 1
sum after first call = 21
hits after three calls = 3
sum after three calls = 39
ctx + value = 142
hits after ctx callback = 4

9
vendors/ffi_callback/ffi_callback.c vendored Normal file
View File

@@ -0,0 +1,9 @@
#include "ffi_callback.h"
int ffi_apply_callback(int (*cb)(int), int value) {
return cb(value);
}
int ffi_apply_callback2(int (*cb)(void *ctx, int v), void *ctx, int v) {
return cb(ctx, v);
}

13
vendors/ffi_callback/ffi_callback.h vendored Normal file
View File

@@ -0,0 +1,13 @@
// C-to-sx callback FFI baseline. C takes a function pointer + a value,
// invokes the callback with the value, and returns whatever the callback
// returned. Mirrors the `app->onInputEvent` pattern in
// library/modules/platform/android.sx where sx installs a handler that
// native_app_glue invokes from its input-event loop.
int ffi_apply_callback(int (*cb)(int), int value);
// Two-arg variant — the actual chess-on-Android shape:
// the callback receives a pointer + a value (mirrors
// onInputEvent(app, event) where both are opaque pointers from
// the C caller's point of view).
int ffi_apply_callback2(int (*cb)(void *ctx, int v), void *ctx, int v);