use rane::ffi::*;
use std::ffi::{c_void, CStr, CString};
const ESPRESSO_BUNDLE: &str = "/System/Library/DuetExpertCenter/Assets/Assets.bundle/AssetData/ATXActionValuationMLModel.mlmodelc";
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== ANEC IR path-fix probe ===\n");
for name in &["AppleNeuralEngine", "ANECompiler", "ANEServices"] {
let path = format!("/System/Library/PrivateFrameworks/{name}.framework/{name}");
let c = CString::new(path).unwrap();
unsafe {
dlopen(c.as_ptr(), RTLD_NOW);
}
}
let mil_text = "program(1, 0)\nfunc main<ios16>(tensor<fp16, [1,16,1,1]> x) -> (tensor<fp16, [1,16,1,1]>) {\n block0() {\n tensor<fp16, [1,16,1,1]> y = relu()[x = x];\n } -> (y)\n}\n";
unsafe {
type DataFn = unsafe extern "C" fn(ObjcId, ObjcSel, *const u8, u64) -> ObjcId;
type DictFn = unsafe extern "C" fn(ObjcId, ObjcSel) -> ObjcId;
type DescFn = unsafe extern "C" fn(ObjcId, ObjcSel, ObjcId, ObjcId, ObjcId) -> ObjcId;
type ModelFn = unsafe extern "C" fn(ObjcId, ObjcSel, ObjcId) -> ObjcId;
type StrFn = unsafe extern "C" fn(ObjcId, ObjcSel) -> ObjcId;
type Utf8Fn = unsafe extern "C" fn(ObjcId, ObjcSel) -> *const std::ffi::c_char;
type ModelFn2 = unsafe extern "C" fn(ObjcId, ObjcSel, ObjcId, u8) -> ObjcId;
type CompileFn = unsafe extern "C" fn(ObjcId, ObjcSel, u32, ObjcId, *mut ObjcId) -> bool;
type StrFn2 = unsafe extern "C" fn(ObjcId, ObjcSel, ObjcId) -> ObjcId;
let cls_data = cls("NSData");
let cls_dict = cls("NSDictionary");
let cls_desc = cls("_ANEInMemoryModelDescriptor");
let cls_model = cls("_ANEInMemoryModel");
let cls_str = cls("NSString");
let dataf: DataFn = std::mem::transmute(objc_msgSend as *const c_void);
let dictf: DictFn = std::mem::transmute(objc_msgSend as *const c_void);
let descf: DescFn = std::mem::transmute(objc_msgSend as *const c_void);
let modelf: ModelFn = std::mem::transmute(objc_msgSend as *const c_void);
let strf: StrFn = std::mem::transmute(objc_msgSend as *const c_void);
let utf8f: Utf8Fn = std::mem::transmute(objc_msgSend as *const c_void);
let modelf2: ModelFn2 = std::mem::transmute(objc_msgSend as *const c_void);
let compilef: CompileFn = std::mem::transmute(objc_msgSend as *const c_void);
let strf2: StrFn2 = std::mem::transmute(objc_msgSend as *const c_void);
let make_nsstr = |s: &str| -> ObjcId {
let c = CString::new(s).unwrap();
strf2(
cls_str as ObjcId,
sel("stringWithUTF8String:"),
c.as_ptr() as ObjcId,
)
};
let bytes = mil_text.as_bytes();
let ns_text = dataf(
cls_data as ObjcId,
sel("dataWithBytes:length:"),
bytes.as_ptr(),
bytes.len() as u64,
);
let empty = dictf(cls_dict as ObjcId, sel("dictionary"));
let desc = descf(
cls_desc as ObjcId,
sel("modelWithMILText:weights:optionsPlist:"),
ns_text,
empty,
std::ptr::null_mut(),
);
let p = (desc as *mut u8).add(8);
*p = 0; let model = modelf(
cls_model as ObjcId,
sel("inMemoryModelWithDescriptor:"),
desc,
);
if model.is_null() {
println!("model=null");
return Ok(());
}
let local_path_ns = strf(model, sel("localModelPath"));
let local_path = {
let c = utf8f(local_path_ns, sel("UTF8String"));
CStr::from_ptr(c).to_string_lossy().into_owned()
};
println!("localModelPath: {local_path}");
let opts = modelf2(
model,
sel("compilerOptionsWithOptions:isCompiledModelCached:"),
empty,
0,
);
println!("compilerOpts: {}", objc_desc(opts));
println!("\n=== Test 1: Real Espresso files at localModelPath ===");
std::fs::create_dir_all(&local_path)?;
for fname in &[
"model.espresso.net",
"model.espresso.shape",
"model.espresso.weights",
] {
let src = format!("{ESPRESSO_BUNDLE}/{fname}");
let dst = format!("{local_path}/{fname}");
match std::fs::copy(&src, &dst) {
Ok(n) => println!(" copied {fname}: {n}B"),
Err(e) => println!(" FAILED to copy {fname}: {e}"),
}
}
let mut err: ObjcId = std::ptr::null_mut();
let ok = compilef(
model,
sel("compileWithQoS:options:error:"),
21,
empty,
&mut err,
);
if ok {
println!(" *** COMPILE SUCCESS! ***");
list_dir(&local_path);
} else {
println!(" error: {:?}", nserror_string(err));
}
for fname in &[
"model.espresso.net",
"model.espresso.shape",
"model.espresso.weights",
] {
std::fs::remove_file(format!("{local_path}/{fname}")).ok();
}
println!("\n=== Test 2: Full bundle files at localModelPath ===");
for entry in std::fs::read_dir(ESPRESSO_BUNDLE)? {
let entry = entry?;
let fname = entry.file_name();
let dst = format!("{local_path}/{}", fname.to_string_lossy());
let src = entry.path();
if src.is_file() {
std::fs::copy(&src, &dst)?;
println!(" copied {}", fname.to_string_lossy());
}
}
let desc2 = descf(
cls_desc as ObjcId,
sel("modelWithMILText:weights:optionsPlist:"),
ns_text,
empty,
std::ptr::null_mut(),
);
let p2 = (desc2 as *mut u8).add(8);
*p2 = 0;
let model2 = modelf(
cls_model as ObjcId,
sel("inMemoryModelWithDescriptor:"),
desc2,
);
if model2.is_null() {
println!(" model2=null");
return Ok(());
}
let mut err2: ObjcId = std::ptr::null_mut();
let ok2 = compilef(
model2,
sel("compileWithQoS:options:error:"),
21,
empty,
&mut err2,
);
if ok2 {
println!(" *** COMPILE SUCCESS! ***");
list_dir(&local_path);
} else {
println!(" error: {:?}", nserror_string(err2));
}
println!("\n=== Test 3: modelWithNetworkDescription: with NSString path ===");
let path_str = make_nsstr(&format!("{local_path}/model.espresso.net"));
let desc3 = descf(
cls_desc as ObjcId,
sel("modelWithNetworkDescription:weights:optionsPlist:"),
path_str,
empty,
std::ptr::null_mut(),
);
if desc3.is_null() {
println!(" desc3=null (NSString path didn't work)");
} else {
let is_mil3 = *(desc3 as *const u8).add(8);
println!(" desc3={desc3:p} isMILModel={is_mil3}");
let model3 = modelf(
cls_model as ObjcId,
sel("inMemoryModelWithDescriptor:"),
desc3,
);
if !model3.is_null() {
let lp3 = strf(model3, sel("localModelPath"));
println!(" localModelPath3 = {}", nsstring_to_str(lp3));
let opts3 = modelf2(
model3,
sel("compilerOptionsWithOptions:isCompiledModelCached:"),
empty,
0,
);
println!(" opts3 = {}", objc_desc(opts3));
let mut err3: ObjcId = std::ptr::null_mut();
let ok3 = compilef(
model3,
sel("compileWithQoS:options:error:"),
21,
empty,
&mut err3,
);
if ok3 {
println!(" *** SUCCESS! ***");
} else {
println!(" err3: {:?}", nserror_string(err3));
}
}
}
println!("\n=== Test 4: modelWithNetworkDescription: with NSData(espresso.net) ===");
let content = std::fs::read(format!("{ESPRESSO_BUNDLE}/model.espresso.net"))?;
let ns_data = dataf(
cls_data as ObjcId,
sel("dataWithBytes:length:"),
content.as_ptr(),
content.len() as u64,
);
let desc4 = descf(
cls_desc as ObjcId,
sel("modelWithNetworkDescription:weights:optionsPlist:"),
ns_data,
empty,
std::ptr::null_mut(),
);
if desc4.is_null() {
println!(" desc4=null (NSData didn't work)");
} else {
let is_mil4 = *(desc4 as *const u8).add(8);
println!(" desc4={desc4:p} isMILModel={is_mil4}");
let model4 = modelf(
cls_model as ObjcId,
sel("inMemoryModelWithDescriptor:"),
desc4,
);
if !model4.is_null() {
let lp4 = strf(model4, sel("localModelPath"));
println!(" localModelPath4 = {}", nsstring_to_str(lp4));
let opts4 = modelf2(
model4,
sel("compilerOptionsWithOptions:isCompiledModelCached:"),
empty,
0,
);
println!(" opts4 = {}", objc_desc(opts4));
let hex4 = strf(model4, sel("hexStringIdentifier"));
let local4 = nsstring_to_str(lp4);
std::fs::create_dir_all(&local4).ok();
for fname in &[
"model.espresso.net",
"model.espresso.shape",
"model.espresso.weights",
] {
let src = format!("{ESPRESSO_BUNDLE}/{fname}");
let dst = format!("{local4}/{fname}");
std::fs::copy(&src, &dst).ok();
}
let mut err4: ObjcId = std::ptr::null_mut();
let ok4 = compilef(
model4,
sel("compileWithQoS:options:error:"),
21,
empty,
&mut err4,
);
if ok4 {
println!(" *** SUCCESS! ***");
list_dir(&local4);
} else {
println!(" err4: {:?}", nserror_string(err4));
}
std::fs::remove_dir_all(&local4).ok();
}
}
std::fs::remove_dir_all(&local_path).ok();
}
Ok(())
}
unsafe fn nsstring_to_str(obj: ObjcId) -> String {
if obj.is_null() {
return "(null)".to_string();
}
type Utf8Fn = unsafe extern "C" fn(ObjcId, ObjcSel) -> *const std::ffi::c_char;
let uf: Utf8Fn = std::mem::transmute(objc_msgSend as *const c_void);
let p = uf(obj, sel("UTF8String"));
if p.is_null() {
return "(null utf8)".to_string();
}
CStr::from_ptr(p).to_string_lossy().into_owned()
}
unsafe fn objc_desc(obj: ObjcId) -> String {
if obj.is_null() {
return "(null)".into();
}
type StrFn = unsafe extern "C" fn(ObjcId, ObjcSel) -> ObjcId;
type Utf8Fn = unsafe extern "C" fn(ObjcId, ObjcSel) -> *const std::ffi::c_char;
let sf: StrFn = std::mem::transmute(objc_msgSend as *const c_void);
let uf: Utf8Fn = std::mem::transmute(objc_msgSend as *const c_void);
let d = sf(obj, sel("description"));
if d.is_null() {
return "(no desc)".into();
}
let c = uf(d, sel("UTF8String"));
if c.is_null() {
return "(null utf8)".into();
}
CStr::from_ptr(c).to_string_lossy().into_owned()
}
fn list_dir(dir: &str) {
if let Ok(entries) = std::fs::read_dir(dir) {
println!(" dir contents:");
for entry in entries.flatten() {
let meta = std::fs::metadata(entry.path()).ok();
let size = meta.map(|m| m.len()).unwrap_or(0);
println!(" {}: {size}B", entry.file_name().to_string_lossy());
}
}
}