Commit Graph

4 Commits

Author SHA1 Message Date
agra
2882748ae6 ffi 2.11 green: DSL call sites on #jni_class-typed receivers lower to JNI dispatch
`inst.method(args)` on a value typed as a foreign-class alias
(`Activity :: #jni_class("android/app/Activity") { getWindow ::
(self: *Self) -> *Window; }` etc.) now lowers to `jni_msg_send`
with descriptor auto-derived from the sx signature, env from the
enclosing `#jni_env` scope (lexical-direct via 2.16b), and slot
interning re-used from Phase 1C.

Touch surface:
- `Lowering` gains `foreign_class_map: StringHashMap(*const
  ForeignClassDecl)` populated in `scanDecls` + `lowerDecls`.
- New `registerForeignClassDecl` records each declared alias; the
  type-bridge fallback already interns the alias as a 0-field
  struct, so `*Activity` resolves cleanly through `getStructTypeName`.
- New `lowerForeignMethodCall` looks up the method in
  `ForeignClassDecl.members`, derives the descriptor via
  `jni_descriptor.deriveMethod` (with a `ClassRegistry` built from
  `foreign_class_map`), and emits `jni_msg_send` directly. Filters
  by runtime — `jni_class`/`jni_interface` lower; `objc_class` etc.
  surface a clear "not yet supported" diagnostic until Phase 3/4.
- `lowerCall`'s method-dispatch arm inserts the foreign-class
  check before the standard struct-method resolution.

JNI descriptor derivation gains `*void → Ljava/lang/Object;` (the
opaque-jobject convention) — common when sx code doesn't have a
precise Java type for the value. Locked in with a unit test.

IR snapshot at `tests/expected/ffi-jni-class-08-call.ir` shows the
full lowering: env from the enclosing fn param, target from the
foreign-class arg, slot-interned `(class, method, sig)` cache
pair, jni_msg_send to `CallObjectMethod` (slot 34). Mangled slot
names `@SX_JNI_CLS_getWindow____Ljava_lang_Object_` confirm the
derived descriptor.

129/129 examples + 16 jni_descriptor unit tests green.
2026-05-20 11:07:41 +03:00
agra
ca840ff6c8 ffi 2.10: #jni_method_descriptor override precedence in deriveMethod
When a `ForeignMethodDecl` carries a non-null
`jni_descriptor_override` (parsed in step 2.6), `deriveMethod`
short-circuits — auto-derivation is skipped entirely and the override
is returned (duplicated through the caller's allocator so ownership
semantics stay uniform regardless of which branch ran).

Two new tests: override beats normal derivation, and override
bypasses cross-class refs that would otherwise fail with
`UnknownClassAlias`. Confirms the escape-hatch semantics from 2.6 —
users can paste an explicit JNI signature when auto-derivation
doesn't match (synthetic methods, ambiguous overloads, unknown-to-sx
JVM internals).

15 jni_descriptor tests pass; 126/126 examples still green.
2026-05-20 10:27:33 +03:00
agra
51882656a5 ffi 2.9: cross-class *Foo resolves via ForeignClassDecl registry
`Context` gains an optional `classes: ?*const ClassRegistry` lookup
(sx alias → foreign path). `writeType`'s pointer-type arm now treats
`*Self` and `*Foo` uniformly: `Self` resolves to `enclosing_path`,
any other named target is looked up in the registry. Missing
registry or missing key both surface as `UnknownClassAlias`.

`DeriveError.CrossClassRefNotYetSupported` retired in favour of
`UnknownClassAlias` — the new error fires for both
"no-registry-provided" and "alias-not-in-registry", giving the
caller (later, sema with a real diagnostic) one error variant to
handle.

Four new unit tests: cross-class resolves with registry, errors
without registry, errors with empty registry, and end-to-end
`deriveMethod` with chained `*Self`/`*Foo` (`getDecorView ::
(self: *Self) -> *View → ()Landroid/view/View;`).

13 jni_descriptor tests pass; 126/126 examples still green.
2026-05-20 10:26:03 +03:00
agra
21c49066e5 ffi 2.8: JNI descriptor derivation + table-driven Zig unit tests
New `src/ir/jni_descriptor.zig`:
  - `writeType(allocator, buf, ctx, type_node)` appends one JNI
    descriptor for an sx type AST node.
  - `deriveMethod(allocator, ctx, method)` returns the full
    `(args)ret` descriptor for a `ForeignMethodDecl`, skipping the
    implicit `self` for instance methods.
  - `Context.enclosing_path` resolves `*Self` to its `L<path>;` form.

Primitive table: void→V, bool→Z, s8/u8→B, s16→S, u16→C, s32→I,
s64→J, f32→F, f64→D. Arrays: `[]T` / `[*]T` / `[N]T` → `[<elem>`.
`*Self` → `L<enclosing>;`. Cross-class `*Foo` → explicit
`CrossClassRefNotYetSupported` error (lands in step 2.9 with the
ForeignClassDecl registry lookup).

Tests in `src/ir/jni_descriptor.test.zig`: primitive table coverage,
void-on-null, *Self, slice, cross-class-fail-fast, plus three
deriveMethod scenarios (instance, static, multi-param, slice param).

Step 2.8 is internal compiler work — derivation isn't observable at
the sx surface until call-site lowering at step 2.11. The cadence
rule's xfail-then-green pattern presupposes a snapshot harness that
doesn't apply to internal-only functions; the rule re-applies at
2.11 where end-to-end observation returns.

zig build test passes; 126/126 examples still green.
2026-05-20 10:24:12 +03:00