ffi M3.2: SxSceneDelegate migrated + #implements protocol conformance
Migrates SxSceneDelegate from the hand-rolled
objc_allocateClassPair + class_addMethod + class_addProtocol
sequence to the declarative form:
SxSceneDelegate :: #objc_class("SxSceneDelegate") {
#extends UIResponder;
#implements UISceneDelegate;
#implements UIWindowSceneDelegate;
scene_willConnectToSession_options :: (self, scene, session, options) { ... }
window :: (self) -> *void { ... }
setWindow :: (self, w) { ... }
}
emit_llvm now honors '#implements' in the class-pair init
constructor — for each #implements ProtocolAlias on the cache
entry's AST, emit before objc_registerClassPair:
proto = objc_getProtocol("ProtocolName")
class_addProtocol(cls, proto)
iOS checks 'class_conformsToProtocol' when instantiating scene
delegates; without the conformance the runtime silently rejects
the class and a default scene with no delegate gets created
instead. The protocol-getter returns null on dead-strip /
runtime mismatch (rare but possible) — the runtime treats
class_addProtocol(cls, null) as a no-op, so no explicit null
check needed.
Method bodies forward to the existing legacy free IMP functions
(uikit_scene_will_connect, uikit_window_getter,
uikit_window_setter) so we don't have to inline the scene-
connect setup logic (~80 lines).
uikit_register_classes is now tiny — just the two remaining
view-class helpers (M3.3 SxGLView + M3.4 SxMetalView). M3.5
deletes the function entirely once those land.
Chess on iOS-sim: board renders, scene delegate fires, touch
events route correctly. 183 example tests + zig build test
green.
This commit is contained in:
@@ -654,6 +654,28 @@ pub const LLVMEmitter = struct {
|
||||
_ = c.LLVMBuildCall2(self.builder, add_method_ty, add_method_fn, &add_args, 4, "");
|
||||
}
|
||||
|
||||
// M2.3 / M3.2 — register `#implements` protocol conformances
|
||||
// BEFORE objc_registerClassPair. iOS checks
|
||||
// `class_conformsToProtocol` when instantiating scene
|
||||
// delegates and other protocol-typed callbacks; without
|
||||
// these the runtime silently rejects the class.
|
||||
//
|
||||
// The protocol may not be present on every SDK / runtime
|
||||
// (dead-strip pruning, version skew), so `objc_getProtocol`
|
||||
// returning null is non-fatal — skip the addProtocol call.
|
||||
const get_proto_fn, const get_proto_ty = self.lazyDeclareCRuntime("objc_getProtocol", &[_]c.LLVMTypeRef{ptr_ty}, ptr_ty, 0);
|
||||
const add_proto_fn, const add_proto_ty = self.lazyDeclareCRuntime("class_addProtocol", &[_]c.LLVMTypeRef{ ptr_ty, ptr_ty }, i8_ty, 0);
|
||||
for (fcd.members) |m| switch (m) {
|
||||
.implements => |proto_alias| {
|
||||
const proto_str_global = self.emitPrivateCString(proto_alias, "OBJC_PROTOCOL_NAME_");
|
||||
var gp_args: [1]c.LLVMValueRef = .{proto_str_global};
|
||||
const proto_val = c.LLVMBuildCall2(self.builder, get_proto_ty, get_proto_fn, &gp_args, 1, "proto");
|
||||
var ap_args: [2]c.LLVMValueRef = .{ cls_val, proto_val };
|
||||
_ = c.LLVMBuildCall2(self.builder, add_proto_ty, add_proto_fn, &ap_args, 2, "");
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
|
||||
// objc_registerClassPair(cls)
|
||||
var reg_args: [1]c.LLVMValueRef = .{cls_val};
|
||||
_ = c.LLVMBuildCall2(self.builder, register_ty, register_fn, ®_args, 1, "");
|
||||
|
||||
Reference in New Issue
Block a user