use crate::error::Result;
use crate::format::io::pod::{self, RawProtocolT64};
use crate::model::addr::Va;
use crate::objc::method::parse_method_list;
use crate::objc::property::{parse_property_list, parse_property_list_with_kind};
use crate::objc::resolve::ObjCResolver;
use crate::objc::types::ObjCProtocol;
pub fn parse_protocol(resolver: &ObjCResolver<'_>, proto_va: Va) -> Result<ObjCProtocol> {
let offset = resolver.va_to_offset(proto_va)?;
let data = resolver.macho().bytes();
let endian = resolver.endian();
let raw: RawProtocolT64 = pod::read_pod(data, offset.as_usize())?;
let protocol_size = endian.interpret_u32(raw.size) as usize;
let name_ptr_offset = offset.as_usize() as u64 + 8;
let name = match resolver.read_pointer_at_offset(name_ptr_offset)? {
Some(va) => resolver.read_cstring(va).unwrap_or("<invalid>").to_string(),
None => "<null>".to_string(),
};
let inst_methods_offset = offset.as_usize() as u64 + 24;
let instance_methods = match resolver.read_pointer_at_offset(inst_methods_offset)? {
Some(va) if va.0 != 0 => parse_method_list(resolver, va).unwrap_or_default(),
_ => Vec::new(),
};
let cls_methods_offset = offset.as_usize() as u64 + 32;
let class_methods = match resolver.read_pointer_at_offset(cls_methods_offset)? {
Some(va) if va.0 != 0 => parse_method_list(resolver, va).unwrap_or_default(),
_ => Vec::new(),
};
let opt_inst_offset = offset.as_usize() as u64 + 40;
let optional_instance_methods = match resolver.read_pointer_at_offset(opt_inst_offset)? {
Some(va) if va.0 != 0 => parse_method_list(resolver, va).unwrap_or_default(),
_ => Vec::new(),
};
let opt_cls_offset = offset.as_usize() as u64 + 48;
let optional_class_methods = match resolver.read_pointer_at_offset(opt_cls_offset)? {
Some(va) if va.0 != 0 => parse_method_list(resolver, va).unwrap_or_default(),
_ => Vec::new(),
};
let props_offset = offset.as_usize() as u64 + 56;
let properties = match resolver.read_pointer_at_offset(props_offset)? {
Some(va) if va.0 != 0 => parse_property_list(resolver, va).unwrap_or_default(),
_ => Vec::new(),
};
let class_properties = if protocol_size >= 96 {
let class_props_offset = offset.as_usize() as u64 + 88;
match resolver.read_pointer_at_offset(class_props_offset) {
Ok(Some(va)) if va.0 != 0 => {
parse_property_list_with_kind(resolver, va, true).unwrap_or_default()
}
_ => Vec::new(),
}
} else {
Vec::new()
};
let protos_offset = offset.as_usize() as u64 + 16;
let adopted_protocols = match resolver.read_pointer_at_offset(protos_offset)? {
Some(va) if va.0 != 0 => parse_protocol_name_list(resolver, va).unwrap_or_default(),
_ => Vec::new(),
};
let mut properties = properties;
properties.extend(class_properties);
Ok(ObjCProtocol {
name,
instance_methods,
class_methods,
optional_instance_methods,
optional_class_methods,
properties,
adopted_protocols,
})
}
pub fn parse_protocol_name_list(resolver: &ObjCResolver<'_>, va: Va) -> Result<Vec<String>> {
let offset = resolver.va_to_offset(va)?;
let data = resolver.macho().bytes();
let endian = resolver.endian();
let count = endian.interpret_u64(pod::read_pod::<u64>(data, offset.as_usize())?) as usize;
if count > 10_000 {
return Ok(Vec::new());
}
let mut names = Vec::with_capacity(count);
for i in 0..count {
let ptr_offset = offset.as_usize() as u64 + 8 + i as u64 * 8;
if let Ok(Some(proto_va)) = resolver.read_pointer_at_offset(ptr_offset) {
if proto_va.0 != 0 {
let name_ptr_offset = match resolver.va_to_offset(proto_va) {
Ok(off) => off.as_usize() as u64 + 8,
Err(_) => continue,
};
if let Ok(Some(name_va)) = resolver.read_pointer_at_offset(name_ptr_offset) {
if let Ok(name) = resolver.read_cstring(name_va) {
names.push(name.to_string());
}
}
}
}
}
Ok(names)
}