ffi 3.2 C1: migrate uikit.sx Foundation utility cluster to #objc_class
First of five Phase-3.2 migration clusters. Foundation utility
classes (NSValue, NSNumber, NSDictionary, NSMutableDictionary, NSSet)
in `library/modules/platform/uikit.sx` move from the explicit
`#objc_call(T)(recv, "selector:", args)` form to declarative
`#foreign #objc_class("Cls") { ... }` blocks with `recv.method(args)`
dispatch.
Classes declared (all near the top of uikit.sx, after the CGRect
struct):
- NSValue → CGRectValue (instance)
- NSNumber → numberWithBool (class), doubleValue +
unsignedLongValue (instance)
- NSDictionary → objectForKey (instance)
- NSMutableDictionary → dictionary (class), setObject_forKey (instance)
- NSSet → anyObject (instance)
Each call site casts the `*void` receiver to the typed foreign-class
pointer before dispatch — the existing `*void` shape is preserved
in the struct fields and outer parameter types; only the dispatch-
local copy is typed. This keeps the diff scoped to call-site
rewrites without rippling type changes through every consumer.
The four `objc_getClass(...)` calls that previously resolved
NSMutableDictionary / NSNumber at runtime are gone — Phase 3.1's
`__sx_objc_class_init` constructor populates the cached class slot
for each declared `#objc_class` at module load via
`OBJC_CLASSLIST_REFERENCES_<Cls>`.
166/166 example tests; chess clean on macOS + Android via
`tools/verify-step.sh` (iOS sim skipped — no booted simulator in
this run; previous full run was green at HEAD~6).
This commit is contained in:
@@ -543,8 +543,18 @@ one fixture. Both `.txt` and `.ir` snapshots locked — a change to
|
|||||||
`deriveObjcSelector` produces one diff that surfaces every affected
|
`deriveObjcSelector` produces one diff that surfaces every affected
|
||||||
case at once via the `OBJC_METH_VAR_NAME_*` constants in the IR.
|
case at once via the `OBJC_METH_VAR_NAME_*` constants in the IR.
|
||||||
|
|
||||||
Open work, in roughly the order they make sense:
|
Phase 3.2 C1 landed: Foundation utility cluster in uikit.sx
|
||||||
- **Phase 3 step 3.2 — C1..C5** — uikit.sx migration, one cluster
|
migrated to declarative `#objc_class` bodies. Five classes declared
|
||||||
|
near the top of the file (NSValue, NSNumber, NSDictionary,
|
||||||
|
NSMutableDictionary, NSSet). Call sites rewritten from
|
||||||
|
`#objc_call(T)(recv, "sel:", args)` to `recv.method(args)` /
|
||||||
|
`Cls.method(args)`. Receivers cast from `*void` to the typed
|
||||||
|
foreign-class pointer at the dispatch site. The `objc_getClass(...)`
|
||||||
|
calls for these classes are gone — the class slot is now populated
|
||||||
|
by emit_llvm's `__sx_objc_class_init` constructor (Phase 3.1).
|
||||||
|
|
||||||
|
Open work:
|
||||||
|
- **Phase 3 step 3.2 — C2..C5** — uikit.sx migration, one cluster
|
||||||
per commit, chess regression after each.
|
per commit, chess regression after each.
|
||||||
test for the default-mangling table. Escape hatch for selectors
|
test for the default-mangling table. Escape hatch for selectors
|
||||||
that don't fit the underscore-split rule (e.g. `tableView_
|
that don't fit the underscore-split rule (e.g. `tableView_
|
||||||
|
|||||||
@@ -52,6 +52,38 @@ CGRect :: struct {
|
|||||||
height: f64;
|
height: f64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Foundation utility classes (Phase 3.2 C1) ─────────────────────────
|
||||||
|
// Declarative `#objc_class` bindings replace the previous
|
||||||
|
// `objc_getClass(...) + #objc_call(T)(...)` pattern. Each method's sx-
|
||||||
|
// side name maps to its Obj-C selector via the default mangling rule
|
||||||
|
// (split on `_`; each piece becomes a keyword with `:`).
|
||||||
|
|
||||||
|
NSValue :: #foreign #objc_class("NSValue") {
|
||||||
|
// CGRect unboxing — returns by value via the sret/HFA path.
|
||||||
|
CGRectValue :: (self: *Self) -> CGRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSNumber :: #foreign #objc_class("NSNumber") {
|
||||||
|
// Class method (no `self: *Self` first param → static dispatch).
|
||||||
|
numberWithBool :: (b: s8) -> *NSNumber;
|
||||||
|
// Instance value extractors.
|
||||||
|
doubleValue :: (self: *Self) -> f64;
|
||||||
|
unsignedLongValue :: (self: *Self) -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary :: #foreign #objc_class("NSDictionary") {
|
||||||
|
objectForKey :: (self: *Self, key: *void) -> *void;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableDictionary :: #foreign #objc_class("NSMutableDictionary") {
|
||||||
|
dictionary :: () -> *NSMutableDictionary;
|
||||||
|
setObject_forKey :: (self: *Self, obj: *void, key: *void);
|
||||||
|
}
|
||||||
|
|
||||||
|
NSSet :: #foreign #objc_class("NSSet") {
|
||||||
|
anyObject :: (self: *Self) -> *void;
|
||||||
|
}
|
||||||
|
|
||||||
// GLenum constants for renderbuffer/framebuffer setup that aren't in opengl.sx's
|
// GLenum constants for renderbuffer/framebuffer setup that aren't in opengl.sx's
|
||||||
// loader path (they live on the framework's symbol table directly).
|
// loader path (they live on the framework's symbol table directly).
|
||||||
GL_RENDERBUFFER :u32: 0x8D41;
|
GL_RENDERBUFFER :u32: 0x8D41;
|
||||||
@@ -356,23 +388,28 @@ uikit_keyboard_will_change_frame :: (self: *void, _cmd: *void, notification: *vo
|
|||||||
if g_uikit_plat == null { return; }
|
if g_uikit_plat == null { return; }
|
||||||
plat := g_uikit_plat;
|
plat := g_uikit_plat;
|
||||||
|
|
||||||
user_info := #objc_call(*void)(notification, "userInfo");
|
user_info_raw := #objc_call(*void)(notification, "userInfo");
|
||||||
if user_info == null { return; }
|
if user_info_raw == null { return; }
|
||||||
|
user_info : *NSDictionary = xx user_info_raw;
|
||||||
|
|
||||||
end_value := #objc_call(*void)(user_info, "objectForKey:",
|
end_value_raw := user_info.objectForKey(ns_string("UIKeyboardFrameEndUserInfoKey".ptr));
|
||||||
ns_string("UIKeyboardFrameEndUserInfoKey".ptr));
|
if end_value_raw == null { return; }
|
||||||
if end_value == null { return; }
|
end_value : *NSValue = xx end_value_raw;
|
||||||
end_rect := #objc_call(CGRect)(end_value, "CGRectValue");
|
end_rect := end_value.CGRectValue();
|
||||||
|
|
||||||
dur_value := #objc_call(*void)(user_info, "objectForKey:",
|
dur_value_raw := user_info.objectForKey(ns_string("UIKeyboardAnimationDurationUserInfoKey".ptr));
|
||||||
ns_string("UIKeyboardAnimationDurationUserInfoKey".ptr));
|
|
||||||
anim_dur : f64 = 0.0;
|
anim_dur : f64 = 0.0;
|
||||||
if dur_value != null { anim_dur = #objc_call(f64)(dur_value, "doubleValue"); }
|
if dur_value_raw != null {
|
||||||
|
dur_value : *NSNumber = xx dur_value_raw;
|
||||||
|
anim_dur = dur_value.doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
curve_value := #objc_call(*void)(user_info, "objectForKey:",
|
curve_value_raw := user_info.objectForKey(ns_string("UIKeyboardAnimationCurveUserInfoKey".ptr));
|
||||||
ns_string("UIKeyboardAnimationCurveUserInfoKey".ptr));
|
|
||||||
curve_int : u64 = 0;
|
curve_int : u64 = 0;
|
||||||
if curve_value != null { curve_int = #objc_call(u64)(curve_value, "unsignedLongValue"); }
|
if curve_value_raw != null {
|
||||||
|
curve_value : *NSNumber = xx curve_value_raw;
|
||||||
|
curve_int = curve_value.unsignedLongValue();
|
||||||
|
}
|
||||||
|
|
||||||
// Screen height in points. The window lives on the connected scene's screen.
|
// Screen height in points. The window lives on the connected scene's screen.
|
||||||
if plat.window == null { return; }
|
if plat.window == null { return; }
|
||||||
@@ -520,10 +557,7 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) {
|
|||||||
// EAGLContext.renderbufferStorage:fromDrawable: (color format,
|
// EAGLContext.renderbufferStorage:fromDrawable: (color format,
|
||||||
// non-retained backing). Without this dict the renderbuffer
|
// non-retained backing). Without this dict the renderbuffer
|
||||||
// allocation silently fails and the framebuffer reports INCOMPLETE.
|
// allocation silently fails and the framebuffer reports INCOMPLETE.
|
||||||
NSMutableDictionary := objc_getClass("NSMutableDictionary".ptr);
|
ns_no := NSNumber.numberWithBool(0);
|
||||||
NSNumber := objc_getClass("NSNumber".ptr);
|
|
||||||
|
|
||||||
ns_no := #objc_call(*void)(NSNumber, "numberWithBool:", xx 0);
|
|
||||||
|
|
||||||
// The EAGL dict keys/values must be the framework-provided NSString
|
// The EAGL dict keys/values must be the framework-provided NSString
|
||||||
// constants (pointer identity is checked) — dlsym them from OpenGLES.
|
// constants (pointer identity is checked) — dlsym them from OpenGLES.
|
||||||
@@ -531,10 +565,10 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) {
|
|||||||
colorformat_key := uikit_extern_nsstring("kEAGLDrawablePropertyColorFormat".ptr);
|
colorformat_key := uikit_extern_nsstring("kEAGLDrawablePropertyColorFormat".ptr);
|
||||||
rgba8_value := uikit_extern_nsstring("kEAGLColorFormatRGBA8".ptr);
|
rgba8_value := uikit_extern_nsstring("kEAGLColorFormatRGBA8".ptr);
|
||||||
|
|
||||||
dict := #objc_call(*void)(NSMutableDictionary, "dictionary");
|
dict := NSMutableDictionary.dictionary();
|
||||||
#objc_call(void)(dict, "setObject:forKey:", ns_no, retained_key);
|
dict.setObject_forKey(xx ns_no, retained_key);
|
||||||
#objc_call(void)(dict, "setObject:forKey:", rgba8_value, colorformat_key);
|
dict.setObject_forKey(rgba8_value, colorformat_key);
|
||||||
#objc_call(void)(plat.gl_layer, "setDrawableProperties:", dict);
|
#objc_call(void)(plat.gl_layer, "setDrawableProperties:", xx dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
// EAGLContext + load_gl were already done in uikit_create_gl_context()
|
// EAGLContext + load_gl were already done in uikit_create_gl_context()
|
||||||
@@ -732,7 +766,8 @@ uikit_touch_location :: (touch: *void, view: *void) -> Point {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uikit_first_touch :: (touches: *void) -> *void {
|
uikit_first_touch :: (touches: *void) -> *void {
|
||||||
#objc_call(*void)(touches, "anyObject");
|
touches_set : *NSSet = xx touches;
|
||||||
|
touches_set.anyObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
uikit_gl_view_touches_began :: (self: *void, _cmd: *void, touches: *void, event: *void) callconv(.c) {
|
uikit_gl_view_touches_began :: (self: *void, _cmd: *void, touches: *void, event: *void) callconv(.c) {
|
||||||
|
|||||||
Reference in New Issue
Block a user