ffi M2.1(b): class methods on sx-defined #objc_class
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.
This commit is contained in:
@@ -622,26 +622,38 @@ pub const LLVMEmitter = struct {
|
||||
};
|
||||
_ = c.LLVMBuildCall2(self.builder, add_ivar_ty, add_ivar_fn, &ivar_args, 5, "");
|
||||
|
||||
// class_addMethod(cls, sel_registerName(sel), imp, encoding)
|
||||
// — register each instance method's IMP trampoline (M1.2 A.4b.iii).
|
||||
// Must run BEFORE objc_registerClassPair; the runtime locks
|
||||
// the method list at registration time on some SDK versions.
|
||||
// Class-method registration (M2.1(b)) and the +alloc IMP
|
||||
// (M1.2 A.5) both target the metaclass. Compute it once
|
||||
// up-front so all metaclass-bound class_addMethod calls
|
||||
// can reference the same LLVM value.
|
||||
//
|
||||
// metaclass = object_getClass(cls). (object_getClass on a
|
||||
// Class returns the metaclass — a Class IS an instance of
|
||||
// its metaclass. Distinct from objc_getClass(name).)
|
||||
const obj_get_class_fn, const obj_get_class_ty = self.lazyDeclareCRuntime("object_getClass", &[_]c.LLVMTypeRef{ptr_ty}, ptr_ty, 0);
|
||||
var ogc_args: [1]c.LLVMValueRef = .{cls_val};
|
||||
const metaclass_val = c.LLVMBuildCall2(self.builder, obj_get_class_ty, obj_get_class_fn, &ogc_args, 1, "metacls");
|
||||
|
||||
// class_addMethod(target, sel_registerName(sel), imp, encoding)
|
||||
// — register each method's IMP trampoline (M1.2 A.4b.iii
|
||||
// + M2.1(b)). Instance methods register on `cls`; class
|
||||
// methods (`is_class`) on the metaclass. Must run BEFORE
|
||||
// objc_registerClassPair; the runtime locks the method
|
||||
// list at registration time on some SDK versions.
|
||||
for (entry_kv.methods) |method| {
|
||||
const sel_str_global = self.emitPrivateCString(method.sel, "OBJC_METH_VAR_NAME_");
|
||||
const enc_str_global = self.emitPrivateCString(method.encoding, "OBJC_METH_VAR_TYPE_");
|
||||
|
||||
// sel = sel_registerName("selector")
|
||||
var sel_args: [1]c.LLVMValueRef = .{sel_str_global};
|
||||
const sel_val = c.LLVMBuildCall2(self.builder, sel_reg_ty, sel_reg_fn, &sel_args, 1, "sel");
|
||||
|
||||
// imp = @__<ClassName>_<methodName>_imp
|
||||
const imp_z = self.alloc.dupeZ(u8, method.imp_name) catch continue;
|
||||
defer self.alloc.free(imp_z);
|
||||
const imp_fn = c.LLVMGetNamedFunction(self.llvm_module, imp_z.ptr);
|
||||
if (imp_fn == null) continue; // trampoline missing — skip
|
||||
if (imp_fn == null) continue;
|
||||
|
||||
// class_addMethod(cls, sel, imp, encoding)
|
||||
var add_args: [4]c.LLVMValueRef = .{ cls_val, sel_val, imp_fn, enc_str_global };
|
||||
const target_cls = if (method.is_class) metaclass_val else cls_val;
|
||||
var add_args: [4]c.LLVMValueRef = .{ target_cls, sel_val, imp_fn, enc_str_global };
|
||||
_ = c.LLVMBuildCall2(self.builder, add_method_ty, add_method_fn, &add_args, 4, "");
|
||||
}
|
||||
|
||||
@@ -696,15 +708,9 @@ pub const LLVMEmitter = struct {
|
||||
defer self.alloc.free(alloc_imp_z);
|
||||
const alloc_imp_fn = c.LLVMGetNamedFunction(self.llvm_module, alloc_imp_z.ptr);
|
||||
if (alloc_imp_fn != null) {
|
||||
// metaclass = object_getClass(cls). (Distinct from
|
||||
// objc_getClass: the latter takes a NAME string and is
|
||||
// for class-object lookup. object_getClass takes an
|
||||
// instance pointer — a Class IS itself an instance of
|
||||
// its metaclass — and returns the isa.)
|
||||
const obj_get_class_fn, const obj_get_class_ty = self.lazyDeclareCRuntime("object_getClass", &[_]c.LLVMTypeRef{ptr_ty}, ptr_ty, 0);
|
||||
var ogc_args: [1]c.LLVMValueRef = .{cls_val};
|
||||
const metaclass_val = c.LLVMBuildCall2(self.builder, obj_get_class_ty, obj_get_class_fn, &ogc_args, 1, "metacls");
|
||||
|
||||
// metaclass_val was computed up-front above (shared
|
||||
// with class-method registration). +alloc is a class
|
||||
// method registered on the metaclass.
|
||||
const alloc_sel_global = self.emitPrivateCString("alloc", "OBJC_METH_VAR_NAME_");
|
||||
const alloc_enc_global = self.emitPrivateCString("@@:", "OBJC_METH_VAR_TYPE_");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user