Commit Graph

12 Commits

Author SHA1 Message Date
agra
b06776d6e9 library: vendors/kb_text_shape + vendors/file_utils; modules/ffi/stb_truetype.sx retired
kb_text_shape (v2.10, JimmyLefevre) had been LOST from the sx tree —
ffi/stb_truetype.sx referenced repo paths that no longer existed (and
nothing runs glyph_cache, so the dangling unit never fired). The
trimmed copy returns from the m3te project as a proper vendor:
curated c/kbts_api.h decls over the full upstream header, README with
provenance, and examples/1627 pinning context + font creation so the
unit compiles and runs in-suite. file_utils (in-house asset-read
helper with the Android AAssetManager hook) gets the same unit shape.

modules/ffi/stb_truetype.sx is gone: glyph_cache imports the three
vendored units (stb_truetype, kb_text_shape, file_utils) directly.
2026-06-12 17:58:23 +03:00
agra
58af806b7a library: vendors/stb_image + vendors/stb_truetype — stb ships with sx
The stb headers move from the repo-root vendors/ (resolvable only
with CWD = sx repo) into library/vendors/ following the sqlite
convention — bindings module + c/ sources + provenance README — so
'#import "vendors/stb_image/stb_image.sx"' (image v2.30 + image_write
v1.16) and '#import "vendors/stb_truetype/stb_truetype.sx"' (v1.26)
work from any consumer via the stdlib search paths. modules/ffi/stb.sx
dissolves into the stb_image vendor; modules/ffi/stb_truetype.sx keeps
its non-stb text-shaping companions and re-imports the vendored unit.
examples/1625 pins a deterministic in-memory BMP decode; examples/1626
pins font init + metric invariants against the system Helvetica.
2026-06-12 17:50:21 +03:00
agra
2097772ec9 library: vendors/sqlite — SQLite ships with sx
The vendored amalgamation (3.53.2, public domain) plus the curated
bindings move from the distribution repo into the sx library:
'#import "vendors/sqlite/sqlite.sx"' gives any sx program SQLite
with no system dependency and no build flags — the bindings declare
the C as a named #import c unit (pinned defines + -O2), compiled
through the object cache and shadow-proof via unit-first resolution.
examples/1624 pins the version and a typed round trip in-suite.
2026-06-12 17:39:26 +03:00
agra
178449b548 ERR/E3.0 (slice 3c): source snippet + caret in traces
Each trace frame now shows the offending source line with a `^` caret
under the column — in the catch-handler formatter, the failable-main C
reporter, and the comptime path.

The source line is embedded at compile time as a 5th Frame field
(line_text), not read from disk at runtime: the file field is a
basename and a runtime read would add a filesystem dependency that
fails under the test harness and on locked-down targets.

- errors.lineAt(src, offset): shared helper for the whole source line.
- Frame gains line_text (mirrored in emit_llvm getFrameStructType,
  trace.sx Frame, sx_trace.c SxFrame). emitTraceFrame embeds it; the
  interp .trace_resolve extracts it from the source map.
- trace.sx (new spaces helper) and the C reporter render the line +
  a col-aligned caret, guarded on a non-empty line_text.

Snapshots 243/244/247/253 regenerated. Gates: zig build, zig build
test, run_examples.sh -> 291 passed.
2026-06-01 15:43:22 +03:00
agra
1b6cbc17e7 ERR/E3.0 (slice 3a): embedded Frame trace resolution
Return-trace frames now resolve to real `func at file:line:col`
in-process — no DWARF, no symbolizer.

- New niladic, span-stamped `.trace_frame` IR op (mirrors is_comptime):
  carries no operands; each backend derives the frame from context.
  lower.zig's placeholderTraceFrame emits it; the existing
  sx_trace_push call consumes it.
- emit_llvm: resolve the op's span + current function to
  {file(basename), line, col, func}, build an interned Frame global
  ({string,i32,i32,string}, strings cached by content), push its
  address (ptrtoint).
- interp: pack (func_id << 32 | span.start) for the comptime resolver
  (slice 3b); never a pointer.
- sx_trace.c report_unhandled derefs SxFrame; trace.sx gains the Frame
  struct, frame_at -> *Frame, and field-reading to_string. Layout
  mirrored in 3 places with cross-ref comments.

Verified JIT + AOT. Snapshots 243/244/247 regenerated (placeholder ->
func at file:line:col). Gates: zig build, zig build test,
run_examples.sh -> 290 passed.
2026-06-01 15:10:46 +03:00
agra
210cf91e37 ERR/E4.2: failable-main wrapper (report + exit 1 on escaping error)
A pure-failable `main` (`-> !` / `-> !Named`) that lets an error reach the
function boundary now exits 1 and prints `error: unhandled error reached
main: error.<tag>` + the return trace to stderr, instead of returning the
raw tag id truncated as the exit code with no diagnostic. Success exits 0;
a `catch`-absorbed error exits 0 (buffer cleared).

Codegen wrapper so JIT and AOT behave identically (no host-side special-
casing):
- emit_llvm.zig: the `.ret` arm detects a failable main and routes to
  new `emitFailableMainRet` — `icmp ne tag, 0` → success block `ret i32 0`
  / error block GEPs the tag name out of the always-linked tag-name table,
  calls `sx_trace_report_unhandled`, `ret i32 1`. main's bare-u32 returns
  (success `ret(0)` + each raise's `ret(tag)`) all funnel through it.
- sx_trace.c: new `sx_trace_report_unhandled(tag, name, name_len)` prints
  the header + surviving frames to stderr (placeholder frame format mirrors
  trace.sx until DWARF/E3.0). Lives next to the buffer it reads.
- lower.zig validateMainSignature: the pure-failable arm sets
  needs_trace_runtime so the AOT path auto-links sx_trace.c even when the
  body emits no other push/clear.

Value-carrying `-> (T, !)` main stays gate-rejected (multi-slot wrapper is
a separate slice). examples/244-failable-main.sx.
2026-06-01 09:48:32 +03:00
agra
51f5277380 ERR/E3.1: thread-local error return-trace ring buffer (runtime)
Add the trace buffer that raise/try push to and catch/or/destructure clear,
following the JNI-TLS precedent exactly (a thread_local IR global doesn't work
under the ORC JIT, which doesn't init TLS for AddObjectFile'd objects).

- library/vendors/sx_trace_runtime/sx_trace.c: a `_Thread_local` fixed-cap ring
  (32 frames) of opaque u64s + C API (push / clear / len / truncated /
  frame_at). Overflow keeps the newest CAP frames and latches `truncated`
  (Zig-style); frame_at returns oldest-to-newest. The frame is opaque — the
  E3.3 formatter dispatches on context (PC at runtime, packed (func_id, offset)
  at comptime).
- build.zig: link the .c into the compiler so the JIT resolves sx_trace_* via
  dlsym (and so the unit test links against it).
- src/runtime_trace.test.zig: exercises push / overflow-survives-newest / clear
  / len / truncated / ordering against the linked C — grounds the buffer logic
  without shipping throwaway sx builtins.
- lower.zig getTraceFids(): lazily declares the sx_trace_push/clear externs +
  sets needs_trace_runtime. Declared now; the raise/try push sites and the
  absorbing clear sites get wired at E3.2.
- core.zig: auto-injects the .c as a #source for AOT when needs_trace_runtime,
  mirroring the JNI env runtime.

Gates: zig build, zig build test (incl. the new buffer tests), bash
tests/run_examples.sh (277 passed; no codegen change this step — lone failure
is the user's uncommitted 213-canonical-map pack WIP).
2026-06-01 08:13:12 +03:00
agra
619d524bac ffi #jni_main R.5: retire legacy NativeActivity surface
Deletes the entire NativeActivity / native_app_glue / ALooper stack
that the previous Android entry path was built around:

  - `examples/99-android-egl-clear.sx` — the demo of the legacy path.
  - `library/modules/platform/android.sx` — `AndroidPlatform.init`,
    `run_frame_loop`, `sx_android_bootstrap`, `g_android_app`, plus
    the ALooper / AInputEvent / ANativeActivity / AConfiguration
    foreign decls that fed them. The JNI helpers (`sx_load_javavm_fn`,
    `sx_android_get_env`, `sx_query_safe_insets_jni`, the
    `ANATIVEACTIVITY_*` offsets) were tied to the ANativeActivity*
    delivered to `android_main` — they're stale now that the OS hands
    sx code a Java Activity directly via `onCreate(JNIEnv*, jobject)`.
  - `library/vendors/sx_android_jni/sx_android_jni.c` — the input-
    handler installer (`sx_android_install_input_handler`), which
    poked NDK app-pointer field offsets that no longer exist.

`library/modules/platform/android_jni.sx` (the `Activity`/`Window`/
`View`/`WindowInsets` `#jni_class` registry used for safe-insets
dispatch) survives — it's standalone declarative bindings useful from
any `#jni_main` onCreate body. Docstring updated to drop the
"imported from android.sx" framing.

131 host / 4 cross / zig build test all green. End-to-end smoke APK
still produces the expected JNI-mangled symbol +
SxApp-extends-Activity dex.

External consumers (chess) will need to migrate their entry from the
`AndroidPlatform.run_frame_loop` model to the `#jni_main` model
(Java-side Activity drives lifecycle; onSurfaceChanged / Choreographer
drive frames via JNI callbacks). That migration is downstream work.
2026-05-20 15:17:24 +03:00
agra
6a3260ff65 ffi 2.16c green: TL fallback via C-helper runtime + always-omit env in #jni_call
`#jni_call` collapses to a single surface — env is *always* implicit:
either picked up from the lexically-enclosing `#jni_env(env) { ... }`
block's Ref (cheap, register-resident, no TL touch) or from the
runtime's thread-local slot via `sx_jni_env_tl_get()` (one fn call
per dispatch). The explicit-env shape is gone — chess and the
existing tests migrate cleanly by wrapping their helper-fn bodies
in `#jni_env(env) { ... }`.

The TL slot lives outside the user's IR module so the LLVM ORC JIT
can load object files cleanly without `orc_rt` for TLS support:

  library/vendors/sx_jni_runtime/sx_jni_env_tl.c:
    static _Thread_local void *sx_jni_env_tl_slot;
    void *sx_jni_env_tl_get(void) { return sx_jni_env_tl_slot; }
    void sx_jni_env_tl_set(void *env) { sx_jni_env_tl_slot = env; }

Linkage:
- sx-the-compiler links the .c file via build.zig so the JIT
  process-symbol generator resolves `sx_jni_env_tl_get`/`_set`.
- AOT targets get the same .c file auto-linked via the lowering
  pass: when lower touches the TL externs, it sets
  `needs_jni_env_tl_runtime`, and `Compilation.lowerToIR` appends a
  synthetic `CImportInfo` to `lowering_extra_c_sources` that
  `collectCImportSources` merges with user-written ones.

Lowering-side changes:
- `getJniEnvTlFids` lazily declares the two externs (parallel
  to `getSelRegisterNameFid`) and flips `needs_jni_env_tl_runtime`.
- `#jni_env(env) { body }` emits save→set→body→restore via three
  `call` ops to the externs; the inner body sees env via the
  lexical-direct stack.
- `lowerJniCall` resolves env from `jni_env_stack` (top) or the TL
  fallback. The explicit-env branch is gone.
- `jni_env_stack_base` tracks per-fn lexical scope so lazy-lowering
  a callee doesn't accidentally see the caller's Ref (Refs are only
  valid inside one fn's instruction stream).

Test migration (mechanical):
- ffi-jni-call-{01..09}: each helper fn wraps `#jni_call(...)`
  bodies in `#jni_env(env) { ... }`. Returning values pass through
  the block as an expression — `#jni_env` now also lowers in
  expression position.

Verified:
- zig build test + tests/run_examples.sh: 130/130 green.
- tests/cross_compile.sh: 3/3 green.
- Chess APK rebuilt + reinstalled on Pixel. Board renders with
  status-bar clearance + info panel intact; no crashes in logcat.
  Safe-insets dispatch through `#jni_env` + lexical-direct now
  fully exercised end-to-end on real hardware.
2026-05-20 13:53:25 +03:00
agra
4ddee931b5 ffi 1.29: retire the C sx_android_query_safe_insets body
Closes the Phase 1D migration for the safe-insets JNI chain. The C
function and its `#foreign` declaration in `android.sx` are gone;
all dispatch now goes through the sx-side `#jni_call` machinery
plus the JavaVM helpers landed in 1.26.

What's gone from `library/vendors/sx_android_jni/sx_android_jni.c`:
- `#include <android/native_activity.h>` and `<jni.h>` (no longer
  needed without the JNI body).
- `sx_android_query_safe_insets` — 55 lines of `(*env)->Foo` chain
  with manual `goto done` early-exit. Migrated to
  `library/modules/platform/android.sx::sx_query_safe_insets_jni`
  in 1.25 (15 lines of `#jni_call`).

What stays:
- `sx_android_install_input_handler` — non-JNI; struct-field
  assignment against `struct android_app`'s `onInputEvent` slot.
  No sx equivalent yet (would need to either land a `#android_app`-
  style intrinsic or hand-roll the offset, neither of which is
  Phase 1 scope).
- `<android/input.h>` and the `struct sx_android_app_min` mirror
  needed by the input-handler installer.

Net diff: -55 lines in the .c file, -1 line `#foreign` decl in
android.sx. Phase 2 (declarative JNI imports) will revisit whether
the .c file can be deleted entirely (the input-handler hop may
move into a different shape).

Verification:
- zig build + zig test + run_examples + cross_compile all green.
  Notable: the previously-failing `ffi-objc-call-12-rect-u64-returns`
  also passes now — looks like the working-tree `#import c` work
  was tidied up alongside.
- chess Android APK rebuilt + reinstalled + launched on Pixel
  device; safe-insets behavior unchanged (board top edge sits below
  the status bar correctly, all pieces in starting positions, no
  status-bar overlap).
2026-05-19 23:13:51 +03:00
agra
c08a749043 ffi 0.7: #import c { #include / #source } via stdlib-path resolution
94/94 regression tests pass (+ffi-07-c-import-block).

Companion C helper lives only at
`library/vendors/sx_ffi_resolve_test/`. Critically NOT in
`sx/vendors/` (the sx repo root) and NOT in the importing
example's directory — so the `vendors/...` paths in this
example are findable solely via the stdlib search branch
(`<exe>/../../library`, `<exe>/../library`, `<exe>/library`).

That branch is the one the JNI insets bridge needs to reach
`library/vendors/sx_android_jni/sx_android_jni.c` without
forcing chess (or any consumer) to vendor an identically-named
copy. The test pins the resolution end-to-end:
  - #include  resolves; clang parses the .h; c_import.zig
    synthesizes #foreign fn decls for `sx_ffi_resolve_test_add` /
    `_mul`.
  - #source   resolves; the .c is compiled into the build's
    object list.
  - sx calls the synthesized decls and prints results.
2026-05-19 11:51:34 +03:00
agra
4849cfb904 android: dpi_scale, scissor, JNI safe-insets, touch input
Four Android UX wins landing together; all verified end-to-end on a
Pixel 7 Pro (board fills width, info-panel text renders, status bar
inset honored, tap-to-select + tap-to-move plays 1. e4).

- AndroidPlatform.init reads density via AConfiguration_getDensity
  (app->config at offset 32) and sets dpi_scale = density / 160. The
  hardcoded 1.0 had been making every logical unit equal one physical
  pixel; ChessBoardView's 520-default size_that_fits fallback then
  rendered at ~half the framebuffer width on the device, and glyphs
  rasterized at literal 11-13 physical pixels were essentially invisible
  on a 2340-tall display.
- gles3.sx set_scissor un-stubbed; with dpi_scale right the renderer
  feeds in valid pixel bounds and the Y-flip math lands inside the
  framebuffer.
- New library/vendors/sx_android_jni/sx_android_jni.c walks
  activity -> window -> decorView -> rootWindowInsets via JNI and
  publishes the system-bar insets. safe_insets() lazy-queries the
  first call after EGL is up (decor view isn't attached at bootstrap).
- sx_android_install_input_handler sets app->onInputEvent; sx-side
  sx_android_input_event translates AMotionEvent DOWN/MOVE/UP/CANCEL
  into existing mouse_down/mouse_moved/mouse_up Events so the chess
  board's tap-to-select + ScrollView drag path Just Works. Coordinates
  divided by dpi_scale so layout-side hit tests match. poll_events
  drains its slice after returning (mirrors the SDL pattern).
- src/imports.zig now routes #import c { #source / #include } paths
  through the same chain as #import (importing dir -> CWD -> stdlib
  search paths). Lets library-owned C helpers like the JNI bridge
  live in sx/library/vendors/ without forcing consumers to vendor a
  copy. Existing CWD-relative consumer layouts (chess's vendors/...)
  still resolve first, so no regression.

86/86 regression tests pass.
2026-05-19 11:09:41 +03:00