// Phase 3 step 3.1 (PLAN-FFI.md): static call `Cls.class_method(args)` // on an `#objc_class` alias lowers to `objc_msg_send` against the class // object (loaded once per module via `objc_getClass` and cached). The // selector is derived by the same default mangling as Phase 3.0 // (`stringWithUTF8String_(s)` → "stringWithUTF8String:"). // // Mirrors the JNI surface (`Alias.new(...)` etc.); the lowering // disambiguates static vs instance by looking at `method.is_static` on // the foreign-class member. // // Pre-3.1: `lowerForeignStaticCall` only handles JNI runtime + the // `new` constructor; any other static call bails. Snapshot pins the // bail diagnostic. #import "modules/std.sx"; #import "modules/compiler.sx"; #import "modules/std/objc.sx"; SxProbeStatic :: #foreign #objc_class("SxProbeStatic") { static answer :: () -> s32; static add :: (a: s32, b: s32) -> s32; } answer_imp :: (self: *void, _cmd: *void) -> s32 callconv(.c) { 42; } add_imp :: (self: *void, _cmd: *void, a: s32, b: s32) -> s32 callconv(.c) { a + b; } main :: () -> s32 { inline if OS == .macos { ns_object := objc_getClass("NSObject".ptr); cls := objc_allocateClassPair(ns_object, "SxProbeStatic".ptr, 0); // class_addMethod on the metaclass — that's where class methods live. metacls := object_getClass(xx cls); class_addMethod(metacls, sel_registerName("answer".ptr), xx answer_imp, "i@:".ptr); class_addMethod(metacls, sel_registerName("add:b:".ptr), xx add_imp, "i@:ii".ptr); objc_registerClassPair(cls); n := SxProbeStatic.answer(); print("answer() = {}\n", n); s := SxProbeStatic.add(7, 35); print("add(7, 35) = {}\n", s); } inline if OS != .macos { print("skipped (not macos)\n"); } 0; }