use crate::{
swift::{
SwiftRuntime,
context::{ContextDescriptorFlags, ContextDescriptorKind},
},
util::{read_i32_le_at, read_u32_le_at, relative_pointer},
};
#[derive(Debug, Clone)]
pub struct ParentContext<'a> {
pub address: u64,
pub flags: ContextDescriptorFlags,
pub name: Option<&'a str>,
}
impl<'a> ParentContext<'a> {
pub fn kind(&self) -> ContextDescriptorKind {
self.flags.kind()
}
}
pub struct ParentChain<'a, 'p> {
pub(crate) rt: &'p SwiftRuntime<'a>,
pub(crate) next_descriptor_va: u64,
}
impl<'a, 'p> ParentChain<'a, 'p> {
pub(crate) fn empty(rt: &'p SwiftRuntime<'a>) -> Self {
Self {
rt,
next_descriptor_va: 0,
}
}
pub(crate) fn starting_at(rt: &'p SwiftRuntime<'a>, parent_va: u64) -> Self {
Self {
rt,
next_descriptor_va: parent_va,
}
}
}
impl<'a, 'p> Iterator for ParentChain<'a, 'p> {
type Item = ParentContext<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.next_descriptor_va == 0 {
return None;
}
let descriptor_va = self.next_descriptor_va;
let header = self.rt.read_bytes(descriptor_va, 8)?;
let flags_raw = read_u32_le_at(header, 0)?;
let parent_rel = read_i32_le_at(header, 4)?;
let flags = ContextDescriptorFlags(flags_raw);
let name = read_context_name(self.rt, descriptor_va, flags);
let parent_slot_va = descriptor_va.checked_add(4)?;
self.next_descriptor_va = if parent_rel == 0 {
0
} else {
relative_pointer(parent_slot_va, parent_rel)
};
Some(ParentContext {
address: descriptor_va,
flags,
name,
})
}
}
fn read_context_name<'a>(
rt: &SwiftRuntime<'a>,
descriptor_va: u64,
flags: ContextDescriptorFlags,
) -> Option<&'a str> {
match flags.kind() {
ContextDescriptorKind::Module
| ContextDescriptorKind::Class
| ContextDescriptorKind::Struct
| ContextDescriptorKind::Enum
| ContextDescriptorKind::Protocol
| ContextDescriptorKind::Extension => {
let slot_va = descriptor_va.checked_add(8)?;
let bytes = rt.read_bytes(slot_va, 4)?;
let rel = read_i32_le_at(bytes, 0)?;
if rel == 0 {
return None;
}
let target = relative_pointer(slot_va, rel);
rt.read_cstr(target)
}
ContextDescriptorKind::Anonymous
| ContextDescriptorKind::OpaqueType
| ContextDescriptorKind::Other(_) => None,
}
}
impl<'a, 'p> crate::swift::TypeDescriptor<'a, 'p> {
pub fn parent(&self) -> ParentChain<'a, 'p> {
if self.parent_va == 0 {
return ParentChain::empty(self.runtime());
}
ParentChain::starting_at(self.runtime(), self.parent_va)
}
pub fn qualified_name(&self) -> String {
let mut hops: Vec<&str> = Vec::new();
for ctx in self.parent() {
if let Some(name) = ctx.name {
hops.push(name);
}
}
hops.reverse();
if hops.is_empty() {
return self.name.to_owned();
}
let mut out = String::new();
for hop in hops {
out.push_str(hop);
out.push('.');
}
out.push_str(self.name);
out
}
}
impl<'a, 'p> crate::swift::SwiftProtocol<'a, 'p> {
pub fn parent(&self) -> ParentChain<'a, 'p> {
if self.parent_va == 0 {
return ParentChain::empty(self.rt);
}
ParentChain::starting_at(self.rt, self.parent_va)
}
pub fn qualified_name(&self) -> String {
let mut hops: Vec<&str> = Vec::new();
for ctx in self.parent() {
if let Some(name) = ctx.name {
hops.push(name);
}
}
hops.reverse();
if hops.is_empty() {
return self.name.to_owned();
}
let mut out = String::new();
for hop in hops {
out.push_str(hop);
out.push('.');
}
out.push_str(self.name);
out
}
}