ffi M3.1 + M1.2 A.3 refactor: self=Obj-C id, self.field via ivar; SxAppDelegate migrated
Two coupled changes that unblock the uikit_register_classes
migration:
1) M1.2 A.3 — body's 'self' is the Obj-C id (opaque), NOT the
state struct. Matches Apple's ObjC semantics where 'self' IS
the object. Cocoa idiom 'xx self → id' works at runtime calls
(addObserver:, etc.); previously the trampoline replaced
'self' with the state-struct pointer, breaking any runtime
call that expected an id.
'*Self' substitution in resolveTypeWithBindings now points at
foreignClassStructType(fcd) — the opaque class stub — instead
of objcDefinedStateStructType(fcd).
'self.field' access on a sx-defined class instance field is
rewritten by lowerFieldAccess to go through the __sx_state
ivar:
state = object_getIvar(self, load(__<Cls>_state_ivar))
val = struct_gep(state, field_idx) → load
Both read (lowerFieldAccess) and write (lowerAssignment) take
this path. Compound ops (+=, -=, etc.) are supported via
storeOrCompound. The lookup is filtered: skip property fields
(those still go through the M2.2 msgSend getter/setter
dispatch) and foreign classes (no state).
New helpers in lower.zig:
- lookupObjcDefinedStateFieldOnPointer — match check.
- lowerObjcDefinedStateForObj — emit the object_getIvar +
ivar-global-load idiom (shared between read + write paths).
- lowerObjcDefinedStateFieldRead — the load path.
Also moved the @llvm.global_ctors registration out of the
sx-defined class-pair init constructor — global_ctors fires
DURING dyld's framework load, before UIKit registers its Obj-C
classes. objc_getClass("UIResponder") returned null, super
was null, objc_registerClassPair crashed. main's entry block
is post-framework-load but pre-user-code — exactly the right
window. New helper injectCtorIntoMain.
2) M3.1 — SxAppDelegate migrated to declarative #objc_class.
uikit_register_classes' hand-rolled objc_allocateClassPair +
class_addMethod for SxAppDelegate is gone; the compiler
synthesises the class at module init. The method bodies
forward to the existing legacy IMP free functions
(uikit_did_finish_launching, uikit_keyboard_will_change_frame)
so we don't have to inline 70+ lines of keyboard-frame logic
right now.
Also adds UIResponder foreign-class declaration and chains
UIView / UITextField to it via #extends UIResponder so the
methods that previously lived on UITextField directly
(becomeFirstResponder etc.) move to their proper home.
Chess on iOS-sim: board renders, full state intact. 183 example
tests + zig build test green.
This commit is contained in:
@@ -140,7 +140,17 @@ UIScreen :: #foreign #objc_class("UIScreen") {
|
||||
bounds :: (self: *Self) -> CGRect;
|
||||
}
|
||||
|
||||
// UIResponder is the root for keyboard / touch / focus dispatch.
|
||||
// Most UIKit classes inherit from it; sx-defined classes that
|
||||
// participate in lifecycle callbacks (delegates, scene delegates)
|
||||
// extend it so the runtime picks up the responder-chain behavior.
|
||||
UIResponder :: #foreign #objc_class("UIResponder") {
|
||||
becomeFirstResponder :: (self: *Self) -> s8;
|
||||
resignFirstResponder :: (self: *Self) -> s8;
|
||||
}
|
||||
|
||||
UIView :: #foreign #objc_class("UIView") {
|
||||
#extends UIResponder;
|
||||
safeAreaInsets :: (self: *Self) -> UIEdgeInsets;
|
||||
addSubview :: (self: *Self, view: *void);
|
||||
layer :: (self: *Self) -> *CALayer;
|
||||
@@ -162,12 +172,29 @@ UIViewController :: #foreign #objc_class("UIViewController") {
|
||||
}
|
||||
|
||||
UITextField :: #foreign #objc_class("UITextField") {
|
||||
alloc :: () -> *UITextField;
|
||||
init :: (self: *Self) -> *UITextField;
|
||||
// Inherited from UIResponder via the runtime; declared here directly
|
||||
// until `#extends UIResponder` lands (Phase 3.4).
|
||||
becomeFirstResponder :: (self: *Self) -> s8;
|
||||
resignFirstResponder :: (self: *Self) -> s8;
|
||||
#extends UIResponder;
|
||||
alloc :: () -> *UITextField;
|
||||
init :: (self: *Self) -> *UITextField;
|
||||
}
|
||||
|
||||
// SxAppDelegate — UIApplicationMain's principal class. Replaces the
|
||||
// M3.1 hand-rolled objc_allocateClassPair + class_addMethod sequence
|
||||
// in uikit_register_classes. The method bodies forward to the
|
||||
// existing legacy IMP free functions so we don't have to inline 70+
|
||||
// lines of keyboard-frame logic here.
|
||||
SxAppDelegate :: #objc_class("SxAppDelegate") {
|
||||
#extends UIResponder;
|
||||
|
||||
application_didFinishLaunchingWithOptions :: (self: *Self, app: *void, opts: *void) -> BOOL {
|
||||
return xx uikit_did_finish_launching(xx self, xx 0, app, opts);
|
||||
}
|
||||
|
||||
sxKeyboardWillChangeFrame :: (self: *Self, notification: *void) {
|
||||
uikit_keyboard_will_change_frame(xx self, xx 0, notification);
|
||||
}
|
||||
|
||||
alloc :: () -> *SxAppDelegate;
|
||||
init :: (self: *SxAppDelegate) -> *SxAppDelegate;
|
||||
}
|
||||
|
||||
// GLenum constants for renderbuffer/framebuffer setup that aren't in opengl.sx's
|
||||
@@ -401,24 +428,19 @@ uikit_chdir_to_bundle :: () {
|
||||
|
||||
uikit_register_classes :: () {
|
||||
inline if OS == .ios {
|
||||
UIResponder := objc_getClass("UIResponder".ptr);
|
||||
SxAppDelegate := objc_allocateClassPair(UIResponder, "SxAppDelegate".ptr, 0);
|
||||
// SxAppDelegate is now declared as `#objc_class("SxAppDelegate")`
|
||||
// (M3.1) — the compiler synthesises its IMPs and class-pair
|
||||
// registration at module init. The old hand-rolled
|
||||
// objc_allocateClassPair + class_addMethod sequence is gone.
|
||||
|
||||
class_addMethod(SxAppDelegate,
|
||||
sel_registerName("application:didFinishLaunchingWithOptions:".ptr),
|
||||
xx uikit_did_finish_launching, "c@:@@".ptr);
|
||||
class_addMethod(SxAppDelegate,
|
||||
sel_registerName("sxKeyboardWillChangeFrame:".ptr),
|
||||
xx uikit_keyboard_will_change_frame, "v@:@".ptr);
|
||||
|
||||
objc_registerClassPair(SxAppDelegate);
|
||||
UIResponder_cls := objc_getClass("UIResponder".ptr);
|
||||
|
||||
// SxSceneDelegate handles the per-scene UI setup. iOS 13+ scene-based
|
||||
// lifecycle: didFinishLaunching is too early for the window — the
|
||||
// UIWindowScene doesn't connect until `scene:willConnectTo:options:`.
|
||||
// The class is named in Info.plist's UIApplicationSceneManifest →
|
||||
// UISceneDelegateClassName.
|
||||
SxSceneDelegate := objc_allocateClassPair(UIResponder, "SxSceneDelegate".ptr, 0);
|
||||
SxSceneDelegate := objc_allocateClassPair(UIResponder_cls, "SxSceneDelegate".ptr, 0);
|
||||
|
||||
class_addMethod(SxSceneDelegate,
|
||||
sel_registerName("scene:willConnectToSession:options:".ptr),
|
||||
|
||||
Reference in New Issue
Block a user