makepad/libs/relax/compiler/tests/objc_macos.rs
Admin b22a714840 makepad run: the Rust JIT (libs/relax) and makepad-remote
The JIT that runs makepad apps from source: lexer, front end, type checker,
lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and
warm/cold caches keyed by compiler stamp, provided crates bound late,
startup images, live patching. User-facing name is `makepad run` (binary
`makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal
library name. `makepad check` reports rustc-style diagnostics with rustc
error codes and checks a whole workspace (`--workspace`). Runs on macOS,
Linux, Windows and Android. makepad-remote: one tool schema for apps and the
WM (app/1): grabs, captures, traces, frame profiles and tweaks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00

181 lines
7.4 KiB
Rust

// macOS only: the Objective-C runtime and system frameworks through the C ABI, the way
// platform/src/os/apple uses them. A class registered at runtime with `extern "C"` method
// IMPs taking and returning structs (HFA rect, 48-byte transform through x8, > 8 arguments
// with stack spill), called through objc_msgSend declared per signature (#[link_name]);
// real Foundation/CoreGraphics code returning structs and narrow integers; a GCD worker
// thread calling back into Relax. Frameworks are found through #[link] (the loader
// dlopens them). Output matches rustc.
#[link(name = "Foundation", kind = "framework")]
extern "C" {}
#[link(name = "CoreGraphics", kind = "framework")]
extern "C" {}
#[repr(C)]
#[derive(Clone, Copy)]
struct Point {
x: f64,
y: f64,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct Rect {
origin: Point,
size: Point,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct Affine {
a: f64,
b: f64,
c: f64,
d: f64,
tx: f64,
ty: f64,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct Range {
location: u64,
length: u64,
}
type Id = *mut u8;
type Sel = *mut u8;
extern "C" {
fn objc_getClass(name: *const u8) -> Id;
fn sel_registerName(name: *const u8) -> Sel;
fn objc_allocateClassPair(sup: Id, name: *const u8, extra: usize) -> Id;
fn objc_registerClassPair(c: Id);
#[link_name = "class_addMethod"]
fn add_rect_method(c: Id, sel: Sel, imp: extern "C" fn(Id, Sel, Rect, f64) -> Rect, types: *const u8) -> bool;
#[link_name = "class_addMethod"]
fn add_affine_method(c: Id, sel: Sel, imp: extern "C" fn(Id, Sel, Affine, f64) -> Affine, types: *const u8) -> bool;
#[link_name = "class_addMethod"]
fn add_many_method(c: Id, sel: Sel, imp: extern "C" fn(Id, Sel, i64, f64, i32, f64, u8, f64, i64, f64, i16, f64, i64, f64, u32, f64, Range) -> f64, types: *const u8) -> bool;
#[link_name = "objc_msgSend"]
fn send_id(obj: Id, sel: Sel) -> Id;
#[link_name = "objc_msgSend"]
fn send_rect_d(obj: Id, sel: Sel, r: Rect, d: f64) -> Rect;
#[link_name = "objc_msgSend"]
fn send_affine(obj: Id, sel: Sel, t: Affine, k: f64) -> Affine;
#[link_name = "objc_msgSend"]
fn send_many(obj: Id, sel: Sel, a: i64, b: f64, c: i32, d: f64, e: u8, f: f64, g: i64, h: f64, i: i16, j: f64, k: i64, l: f64, m: u32, n: f64, o: Range) -> f64;
#[link_name = "objc_msgSend"]
fn send_value_with_rect(cls: Id, sel: Sel, r: Rect) -> Id;
#[link_name = "objc_msgSend"]
fn send_rect(obj: Id, sel: Sel) -> Rect;
#[link_name = "objc_msgSend"]
fn send_str(cls: Id, sel: Sel, s: *const u8) -> Id;
#[link_name = "objc_msgSend"]
fn send_u64(obj: Id, sel: Sel) -> u64;
#[link_name = "objc_msgSend"]
fn send_char_at(obj: Id, sel: Sel, i: u64) -> u16;
#[link_name = "objc_msgSend"]
fn send_substr(obj: Id, sel: Sel, r: Range) -> Id;
fn CGRectInset(r: Rect, dx: f64, dy: f64) -> Rect;
fn CGRectIntersection(a: Rect, b: Rect) -> Rect;
fn CGRectContainsPoint(r: Rect, p: Point) -> bool;
fn CGAffineTransformMakeRotation(angle: f64) -> Affine;
fn CGAffineTransformConcat(a: Affine, b: Affine) -> Affine;
fn dispatch_get_global_queue(identifier: i64, flags: u64) -> Id;
fn dispatch_async_f(queue: Id, ctx: *mut u8, work: extern "C" fn(*mut u8));
fn dispatch_semaphore_create(value: i64) -> Id;
fn dispatch_semaphore_signal(s: Id) -> i64;
fn dispatch_semaphore_wait(s: Id, timeout: u64) -> i64;
fn pthread_self() -> usize;
}
extern "C" fn inset_rect(_this: Id, _sel: Sel, r: Rect, d: f64) -> Rect {
Rect { origin: Point { x: r.origin.x + d, y: r.origin.y + d }, size: Point { x: r.size.x - 2.0 * d, y: r.size.y - 2.0 * d } }
}
extern "C" fn scale_affine(_this: Id, _sel: Sel, t: Affine, k: f64) -> Affine {
Affine { a: t.a * k, b: t.b * k, c: t.c * k, d: t.d * k, tx: t.tx + k, ty: t.ty - k }
}
extern "C" fn many_args(_this: Id, _sel: Sel, a: i64, b: f64, c: i32, d: f64, e: u8, f: f64, g: i64, h: f64, i: i16, j: f64, k: i64, l: f64, m: u32, n: f64, o: Range) -> f64 {
(a + c as i64 + e as i64 + g + i as i64 + k + m as i64) as f64 + b + d + f + h + j + l + n + (o.location * 1000 + o.length) as f64
}
fn cstr(s: &str) -> *const u8 {
s as *const str as *const u8
}
fn p(r: Rect) {
println!("rect {} {} {} {}", r.origin.x, r.origin.y, r.size.x, r.size.y);
}
fn pa(t: Affine) {
println!("affine {:.6} {:.6} {:.6} {:.6} {} {}", t.a, t.b, t.c, t.d, t.tx, t.ty);
}
#[test]
fn runtime_class_with_struct_methods() {
unsafe {
let ns_object = objc_getClass(cstr("NSObject\0"));
let cls = objc_allocateClassPair(ns_object, cstr("RelaxTestObject\0"), 0);
let s_inset = sel_registerName(cstr("insetRect:by:\0"));
let s_scale = sel_registerName(cstr("scaleAffine:by:\0"));
let s_many = sel_registerName(cstr("many:\0"));
add_rect_method(cls, s_inset, inset_rect, cstr("v@:\0"));
add_affine_method(cls, s_scale, scale_affine, cstr("v@:\0"));
add_many_method(cls, s_many, many_args, cstr("v@:\0"));
objc_registerClassPair(cls);
let obj = send_id(send_id(cls, sel_registerName(cstr("alloc\0"))), sel_registerName(cstr("init\0")));
p(send_rect_d(obj, s_inset, Rect { origin: Point { x: 1.0, y: 2.0 }, size: Point { x: 30.0, y: 40.0 } }, 2.5));
pa(send_affine(obj, s_scale, Affine { a: 1.0, b: 2.0, c: 3.0, d: 4.0, tx: 5.0, ty: 6.0 }, 0.5));
println!("many {}", send_many(obj, s_many, 1, 0.5, -2, 0.25, 200, 0.125, 3, 1.5, -4, 2.5, 5, 3.5, 4000000000, 4.5, Range { location: 7, length: 9 }));
}
}
#[test]
fn foundation_and_coregraphics() {
unsafe {
let r = Rect { origin: Point { x: 10.0, y: 20.0 }, size: Point { x: 300.0, y: 200.0 } };
let ns_value = objc_getClass(cstr("NSValue\0"));
let v = send_value_with_rect(ns_value, sel_registerName(cstr("valueWithRect:\0")), r);
p(send_rect(v, sel_registerName(cstr("rectValue\0"))));
let s = send_str(objc_getClass(cstr("NSString\0")), sel_registerName(cstr("stringWithUTF8String:\0")), cstr("Kernel\u{e9}\0"));
let len = send_u64(s, sel_registerName(cstr("length\0")));
let c = send_char_at(s, sel_registerName(cstr("characterAtIndex:\0")), 6);
let sub = send_substr(s, sel_registerName(cstr("substringWithRange:\0")), Range { location: 2, length: 4 });
println!("nsstring len {} char6 {} sub len {}", len, c, send_u64(sub, sel_registerName(cstr("length\0"))));
p(CGRectInset(r, 5.0, 7.5));
p(CGRectIntersection(r, Rect { origin: Point { x: 100.0, y: 0.0 }, size: Point { x: 400.0, y: 50.0 } }));
println!("contains {} {}", CGRectContainsPoint(r, Point { x: 11.0, y: 21.0 }), CGRectContainsPoint(r, Point { x: 9.0, y: 21.0 }));
let t = CGAffineTransformConcat(CGAffineTransformMakeRotation(0.5), Affine { a: 2.0, b: 0.0, c: 0.0, d: 3.0, tx: 4.0, ty: 5.0 });
pa(t);
}
}
static mut WORK_THREAD: usize = 0;
static mut WORK_SUM: u64 = 0;
extern "C" fn gcd_work(ctx: *mut u8) {
unsafe {
WORK_THREAD = pthread_self();
let mut s = 0u64;
let mut i = 0u64;
while i < 1000 {
s += i * i;
i += 1;
}
WORK_SUM = s;
dispatch_semaphore_signal(ctx);
}
}
#[test]
fn gcd_worker_calls_back() {
unsafe {
let sem = dispatch_semaphore_create(0);
dispatch_async_f(dispatch_get_global_queue(0, 0), sem, gcd_work);
dispatch_semaphore_wait(sem, u64::MAX);
println!("gcd sum {} other thread {}", WORK_SUM, WORK_THREAD != pthread_self());
}
}