Files
sx/examples/143-objc-class-registration.sx
agra b98a22e3f9 ffi M1.2 A.4: emitObjcDefinedClassInit class-pair registration
For every sx-defined '#objc_class', emit a module-init constructor
that registers the class with the Obj-C runtime at module load.
Pattern mirrors the Phase 3.1 emitObjcClassInit companion:
'@llvm.global_ctors' + ORC-JIT main injection.

Constructor body, per cache entry:

  super = objc_getClass("<ParentName>")  // default NSObject
  cls   = objc_allocateClassPair(super, "<ClassName>", 0)
  objc_registerClassPair(cls)

Parent is read from the foreign_class_decl's '.extends' member;
absent ⇒ NSObject (matches M1.2 A.0 spec). Class-name strings
go through new emitPrivateCString helper that mirrors the
selector-init / class-init shape.

Two new small helpers extracted while we were here:
- lazyDeclareCRuntime — declare-once extern wrapper for Obj-C
  runtime APIs.
- appendModuleCtor — append-or-create global_ctors + ORC-JIT
  injection, factored out of emitObjcClassInit.

143-objc-class-registration.sx exercises the round-trip on
macOS: after main starts, objc_getClass("SxFoo".ptr) returns
non-null. Runs against the real Obj-C runtime.

142's IR snapshot updated — the constructor + ctors metadata
are now part of the expected shape.

DEFERRED (A.4b): method-IMP registration (class_addMethod with
a C-ABI trampoline that reads __sx_state ivar and calls the sx
body). DEFERRED (A.5+): synthesized +alloc / -dealloc IMPs and
the '__sx_state' ivar setup.

172 example tests pass (+1 from 143). zig build test green.
2026-05-25 22:14:31 +03:00

44 lines
1.2 KiB
Plaintext

// M1.2 A.4 — class-pair registration with the Obj-C runtime.
//
// Every sx-defined '#objc_class' produces a module-init constructor
// (registered in '@llvm.global_ctors' AND injected at the top of
// 'main' for the ORC JIT path) that calls:
//
// super = objc_getClass("NSObject")
// cls = objc_allocateClassPair(super, "SxFoo", 0)
// objc_registerClassPair(cls)
//
// After the constructor runs, 'objc_getClass("SxFoo")' returns the
// freshly registered class — the round-trip we verify below.
//
// Methods, the '__sx_state' ivar, and the '+alloc' / '-dealloc'
// overrides land in A.4b / A.5 / A.6; this slice just makes the
// class EXIST in the runtime.
#import "modules/std.sx";
#import "modules/compiler.sx";
#import "modules/std/objc.sx";
SxFoo :: #objc_class("SxFoo") {
counter: s32;
bump :: (self: *Self) {
self.counter += 1;
}
}
main :: () -> s32 {
inline if OS == .macos {
cls : Class = objc_getClass("SxFoo".ptr);
if cls == null {
print("FAIL: SxFoo not registered\n");
return 1;
}
print("registered: SxFoo\n");
}
inline if OS != .macos {
print("registered: SxFoo\n");
}
0;
}