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
|
||||
case at once via the `OBJC_METH_VAR_NAME_*` constants in the IR.
|
||||
|
||||
Open work, in roughly the order they make sense:
|
||||
- **Phase 3 step 3.2 — C1..C5** — uikit.sx migration, one cluster
|
||||
Phase 3.2 C1 landed: Foundation utility cluster in uikit.sx
|
||||
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.
|
||||
test for the default-mangling table. Escape hatch for selectors
|
||||
that don't fit the underscore-split rule (e.g. `tableView_
|
||||
|
||||
@@ -52,6 +52,38 @@ CGRect :: struct {
|
||||
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
|
||||
// loader path (they live on the framework's symbol table directly).
|
||||
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; }
|
||||
plat := g_uikit_plat;
|
||||
|
||||
user_info := #objc_call(*void)(notification, "userInfo");
|
||||
if user_info == null { return; }
|
||||
user_info_raw := #objc_call(*void)(notification, "userInfo");
|
||||
if user_info_raw == null { return; }
|
||||
user_info : *NSDictionary = xx user_info_raw;
|
||||
|
||||
end_value := #objc_call(*void)(user_info, "objectForKey:",
|
||||
ns_string("UIKeyboardFrameEndUserInfoKey".ptr));
|
||||
if end_value == null { return; }
|
||||
end_rect := #objc_call(CGRect)(end_value, "CGRectValue");
|
||||
end_value_raw := user_info.objectForKey(ns_string("UIKeyboardFrameEndUserInfoKey".ptr));
|
||||
if end_value_raw == null { return; }
|
||||
end_value : *NSValue = xx end_value_raw;
|
||||
end_rect := end_value.CGRectValue();
|
||||
|
||||
dur_value := #objc_call(*void)(user_info, "objectForKey:",
|
||||
ns_string("UIKeyboardAnimationDurationUserInfoKey".ptr));
|
||||
dur_value_raw := user_info.objectForKey(ns_string("UIKeyboardAnimationDurationUserInfoKey".ptr));
|
||||
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:",
|
||||
ns_string("UIKeyboardAnimationCurveUserInfoKey".ptr));
|
||||
curve_value_raw := user_info.objectForKey(ns_string("UIKeyboardAnimationCurveUserInfoKey".ptr));
|
||||
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.
|
||||
if plat.window == null { return; }
|
||||
@@ -520,10 +557,7 @@ uikit_scene_will_connect_ios :: (delegate: *void, scene: *void) {
|
||||
// EAGLContext.renderbufferStorage:fromDrawable: (color format,
|
||||
// non-retained backing). Without this dict the renderbuffer
|
||||
// allocation silently fails and the framebuffer reports INCOMPLETE.
|
||||
NSMutableDictionary := objc_getClass("NSMutableDictionary".ptr);
|
||||
NSNumber := objc_getClass("NSNumber".ptr);
|
||||
|
||||
ns_no := #objc_call(*void)(NSNumber, "numberWithBool:", xx 0);
|
||||
ns_no := NSNumber.numberWithBool(0);
|
||||
|
||||
// The EAGL dict keys/values must be the framework-provided NSString
|
||||
// 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);
|
||||
rgba8_value := uikit_extern_nsstring("kEAGLColorFormatRGBA8".ptr);
|
||||
|
||||
dict := #objc_call(*void)(NSMutableDictionary, "dictionary");
|
||||
#objc_call(void)(dict, "setObject:forKey:", ns_no, retained_key);
|
||||
#objc_call(void)(dict, "setObject:forKey:", rgba8_value, colorformat_key);
|
||||
#objc_call(void)(plat.gl_layer, "setDrawableProperties:", dict);
|
||||
dict := NSMutableDictionary.dictionary();
|
||||
dict.setObject_forKey(xx ns_no, retained_key);
|
||||
dict.setObject_forKey(rgba8_value, colorformat_key);
|
||||
#objc_call(void)(plat.gl_layer, "setDrawableProperties:", xx dict);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
#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) {
|
||||
|
||||
Reference in New Issue
Block a user