Bodied methods without a '*Self' first param (parser marks
is_static=true) are now registered as Obj-C CLASS methods on
the metaclass.
Each such method gets:
- A synthesized FnDecl + body lowering through the existing
M1.2 A.2 path.
- A C-ABI trampoline 'emitObjcDefinedClassStaticImp' — same
shape as the instance trampoline but skips the __sx_state
ivar read (no instance state) and passes only
'__sx_default_context' (plus user args) to the sx body.
- An entry in ObjcDefinedMethodEntry with 'is_class=true'.
emit_llvm's class-pair init constructor now computes the
metaclass once up-front (via object_getClass(cls)) and shares
it between the +alloc IMP registration (M1.2 A.5) and the
M2.1(b) class-method registrations. The per-method registration
loop picks the target via 'method.is_class ? metaclass : cls'.
149-objc-class-method-static-imp.sx end-to-end on macOS:
SxFoo :: #objc_class("SxFoo") {
answer :: () -> s32 { return 42; }
}
// [SxFoo answer] via objc_msgSend → 42
// class_getClassMethod(SxFoo, sel_answer) → non-null
Still TODO for M2.1: the (a) class-LEVEL constant form
'layerClass :: Class = CAEAGLLayer.class();' — needs parser
extension to recognize 'name :: Type = expr;' inside #objc_class
blocks, plus lazy-init-slot synthesis.
179 example tests pass (+1). zig build test green.
49 lines
1.6 KiB
Plaintext
49 lines
1.6 KiB
Plaintext
// M2.1(b) — class methods (no `*Self` first param) on a
|
|
// sx-defined `#objc_class`.
|
|
//
|
|
// The user declares a method without `self: *Self`. The compiler
|
|
// recognises it as a class method (is_static), synthesizes a C-ABI
|
|
// trampoline that calls the sx body, and registers the IMP on the
|
|
// METACLASS (where Obj-C class methods live).
|
|
//
|
|
// Verifies the runtime side:
|
|
// 1. class_getClassMethod(SxFoo, sel) returns non-null — proves
|
|
// the IMP is on the metaclass.
|
|
// 2. objc_msgSend(SxFoo, sel) invokes the IMP and returns the
|
|
// sx body's result.
|
|
|
|
#import "modules/std.sx";
|
|
#import "modules/compiler.sx";
|
|
#import "modules/std/objc.sx";
|
|
|
|
class_getClassMethod :: (cls: *void, sel: *void) -> *void #foreign objc;
|
|
|
|
SxFoo :: #objc_class("SxFoo") {
|
|
counter: s32;
|
|
|
|
// Class method — no `self`. Returns 42.
|
|
answer :: () -> s32 { return 42; }
|
|
}
|
|
|
|
main :: () -> s32 {
|
|
inline if OS == .macos {
|
|
cls : Class = objc_getClass("SxFoo".ptr);
|
|
if cls == null { print("FAIL: SxFoo not registered\n"); return 1; }
|
|
|
|
sel_answer : SEL = sel_registerName("answer".ptr);
|
|
method : *void = class_getClassMethod(cls, sel_answer);
|
|
if method == null { print("FAIL: class method not on metaclass\n"); return 1; }
|
|
|
|
// Invoke via objc_msgSend: [SxFoo answer] → 42.
|
|
msg_fn : (cls: *void, sel: *void) -> s32 callconv(.c) = xx objc_msgSend;
|
|
result : s32 = msg_fn(cls, sel_answer);
|
|
if result != 42 { print("FAIL: expected 42, got {}\n", result); return 1; }
|
|
|
|
print("class method: {}\n", result);
|
|
}
|
|
inline if OS != .macos {
|
|
print("class method: 42\n");
|
|
}
|
|
0;
|
|
}
|