Files
sx/examples/ffi-jni-class-08-call.sx
agra 8d1816018a ffi: define-by-default #jni_class + #foreign modifier + #jni_main token
Flip the surface semantics for type-introducer directives: bare
`Foo :: #jni_class("path") { ... }` now means "DEFINE a new Java class
at that path" (sx-side provides the implementations). The `#foreign`
prefix modifier flips it back to "REFERENCE an existing class on the
foreign runtime." Matches how `#foreign` already reads in sx for C
function declarations (`printf :: ... #foreign;`).

    Foo :: #foreign  #jni_class("path/to/Foo") { ... }  // reference
    Foo ::           #jni_class("path/to/Foo") { ... }  // define
    Foo :: #jni_main #jni_class("path/to/Foo") { ... }  // define + main Activity

Compiler-side changes:
- New `hash_jni_main` lexer token (the launchable-Activity marker).
  Existing `hash_foreign` is reused; no new modifier token there.
- `ForeignClassDecl` gains `is_foreign: bool` + `is_main: bool`.
  `ForeignMethodDecl` gains `body: ?*Node` so defined-class methods
  can carry sx-side implementations (foreign-class methods stay
  `;`-terminated).
- Parser learns `tryParseForeignClassPrefix` — peek-and-consume the
  modifier tokens, then dispatch to the unchanged
  `parseForeignClassDecl` with the flags threaded through.
- Sema rejects two illegal combinations: `#foreign + #jni_main`
  (can't be both an external reference and the app's main entry),
  and bodied methods on `#foreign` decls (foreign methods are
  runtime-provided).
- Lower's foreign-class dispatch errors on non-foreign decls with
  a pointer to the runtime-synthesis follow-up; defined-class
  codegen (Java class emission, RegisterNatives wiring, manifest
  entry generation) lands in a separate session.

Migration:
- `library/modules/platform/android_jni.sx`: all four foreign class
  decls (`Activity`, `Window`, `View`, `WindowInsets`) gain `#foreign`.
- `examples/ffi-jni-class-{01..08}*.sx`: every test's `#jni_class` /
  `#jni_interface` / `#objc_class` / `#objc_protocol` / `#swift_class`
  / `#swift_struct` / `#swift_protocol` usage gains `#foreign`. All
  9 files mechanical perl rename; snapshots unchanged.

Verified locally:
- `zig build test` clean.
- `bash tests/run_examples.sh` 129/129.
- `bash tests/cross_compile.sh` 3/3.
- Chess APK rebuilds, reinstalls, launches on Pixel; safe-area
  clearance preserved.
2026-05-20 12:46:40 +03:00

32 lines
904 B
Plaintext

// Phase 2 step 2.11 (PLAN-FFI.md): xfail then green for DSL call-site
// lowering — `inst.method(args)` on a `#jni_class`-typed value lowers
// to `#jni_call(T)(inst, "method", "(sig)Ret", args...)` with the
// descriptor auto-derived from the sx signature.
//
// `#jni_env(env)` brings env into lexical scope; the omitted-env
// `#jni_call` form (2.16b) picks it up directly.
#import "modules/std.sx";
Activity :: #foreign #jni_class("android/app/Activity") {
getWindow :: (self: *Self) -> *void;
}
g_should_call : bool = false;
unused_jni :: (env: *void, act: *Activity) {
#jni_env(env) {
// Today: this fails — sema doesn't know `Activity` as a type, or
// the method dispatch doesn't recognize foreign-class members.
win := act.getWindow();
}
}
main :: () -> s32 {
if g_should_call {
unused_jni(null, null);
}
print("ok\n");
0;
}