use super::*;
use core::ffi::c_void;
pub unsafe fn get_view_wt(meta: *const c_void) -> *const c_void {
unsafe { get_view_wt_for_proto(meta, resolve::view_proto()) }
}
pub unsafe fn get_view_wt_for_proto(meta: *const c_void, proto: *const c_void) -> *const c_void {
use std::collections::HashMap;
use std::sync::{Mutex, OnceLock};
static CACHE: OnceLock<Mutex<HashMap<(usize, usize), usize>>> = OnceLock::new();
let cache = CACHE.get_or_init(|| Mutex::new(HashMap::new()));
let key = (meta as usize, proto as usize);
if let Ok(map) = cache.lock() {
if let Some(&hit) = map.get(&key) {
return hit as *const c_void;
}
}
let wt = unsafe { conforms_to_protocol(meta, proto) };
if !wt.is_null() {
if let Ok(mut map) = cache.lock() {
map.insert(key, wt as usize);
}
}
wt
}
unsafe fn conforms_to_protocol(meta: *const c_void, proto: *const c_void) -> *const c_void {
static SYM: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
unsafe {
let f = *SYM.get_or_init(|| {
libc::dlsym((-2isize) as *mut c_void, c"swift_conformsToProtocol".as_ptr()) as usize
});
debug_assert!(f != 0, "swift_conformsToProtocol");
type ConformsFn = unsafe extern "C" fn(*const c_void, *const c_void) -> *const c_void;
let f: ConformsFn = core::mem::transmute(f as *const c_void);
f(meta, proto)
}
}
unsafe extern "C" {
fn swift_getTupleTypeMetadata(
request: usize,
flags: usize,
elements: *const *const c_void,
labels: *const u8,
proposed_witnesses: *const c_void,
) -> *const c_void;
}
pub unsafe fn metadata0(accessor: *const c_void) -> *const c_void {
unsafe {
type Acc = unsafe extern "C" fn(usize) -> *const c_void;
let f: Acc = core::mem::transmute(accessor);
f(0)
}
}
pub unsafe fn generic_metadata1(accessor: *const c_void, a0: *const c_void) -> *const c_void {
unsafe {
type Acc = unsafe extern "C" fn(usize, *const c_void) -> *const c_void;
let f: Acc = core::mem::transmute(accessor);
f(0, a0)
}
}
pub unsafe fn generic_metadata2(
accessor: *const c_void,
a0: *const c_void,
a1: *const c_void,
) -> *const c_void {
unsafe {
type Acc = unsafe extern "C" fn(usize, *const c_void, *const c_void) -> *const c_void;
let f: Acc = core::mem::transmute(accessor);
f(0, a0, a1)
}
}
pub unsafe fn tuple_metadata(elements: &[*const c_void]) -> *const c_void {
unsafe {
swift_getTupleTypeMetadata(
0,
elements.len(),
elements.as_ptr(),
core::ptr::null(),
core::ptr::null(),
)
}
}
pub unsafe fn generic_metadata_from_descriptor(
descriptor: *const c_void,
args: &[*const c_void],
) -> *const c_void {
unsafe {
type Get = unsafe extern "C" fn(usize, *const *const c_void, *const c_void) -> *const c_void;
static SYM: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
let f = *SYM.get_or_init(|| {
libc::dlsym((-2isize) as *mut c_void, c"swift_getGenericMetadata".as_ptr()) as usize
});
assert!(f != 0, "swift_getGenericMetadata");
let get: Get = core::mem::transmute(f as *const c_void);
get(0, args.as_ptr(), descriptor)
}
}
pub unsafe fn generic_metadata_from(
known: *const c_void,
args: &[*const c_void],
) -> *const c_void {
unsafe {
let descriptor = *(known as *const *const c_void).add(1);
type Get = unsafe extern "C" fn(usize, *const *const c_void, *const c_void) -> *const c_void;
static SYM: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
let f = *SYM.get_or_init(|| {
libc::dlsym((-2isize) as *mut c_void, c"swift_getGenericMetadata".as_ptr()) as usize
});
assert!(f != 0, "swift_getGenericMetadata");
let get: Get = core::mem::transmute(f as *const c_void);
get(0, args.as_ptr(), descriptor)
}
}
pub unsafe fn tuple_element_offsets(metadata: *const c_void, count: usize) -> Vec<usize> {
unsafe {
debug_assert_eq!(
*(metadata as *const usize).add(1),
count,
"tuple metadata has a different number of elements than asked for",
);
let elements = (metadata as *const u8).add(24) as *const usize;
(0..count).map(|i| *elements.add(i * 2 + 1)).collect()
}
}
pub unsafe fn type_size(metadata: *const c_void) -> usize {
unsafe {
(*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata)).size
}
}
pub unsafe fn type_stride(metadata: *const c_void) -> usize {
unsafe {
(*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata)).stride
}
}
pub unsafe fn type_bytes(metadata: *const c_void) -> usize {
unsafe { type_stride(metadata).max(type_size(metadata)).max(1) }
}
pub unsafe fn type_destroy(metadata: *const c_void, value: *mut u8) {
unsafe {
let vwt = swift_runtime_sys::SwiftABI::get_value_witness_table(metadata);
((*vwt).destroy)(value as *mut _, metadata as *const _);
}
}
pub unsafe fn swift_array(elem_meta: *const c_void, count: usize, bytes: &[u8]) -> u64 {
let stride = unsafe { type_stride(elem_meta) };
debug_assert!(bytes.len() >= stride * count);
let (array, buf): (u64, u64);
unsafe {
core::arch::asm!("blr {f}", f = in(reg) resolve::array_alloc_uninit(),
in("x0") count as u64,
in("x1") elem_meta,
lateout("x0") array, lateout("x1") buf,
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x20") _, lateout("x21") _, clobber_abi("C"),
);
core::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, stride * count);
}
array
}
pub unsafe fn swift_set(
elem_meta: *const c_void,
hashable_wt: *const c_void,
count: usize,
bytes: &[u8],
) -> u64 {
unsafe {
let array = swift_array(elem_meta, count, bytes);
let set_meta = generic_metadata2(resolve::set_ma(), elem_meta, hashable_wt);
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::set_array_literal_init(),
inlateout("x0") array => out,
in("x1") elem_meta,
in("x2") hashable_wt,
in("x20") set_meta,
lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
lateout("x7") _, lateout("x8") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x21") _, clobber_abi("C"),
);
out
}
}
pub unsafe fn set_count(set: u64, elem_meta: *const c_void, hashable_wt: *const c_void) -> usize {
let count: i64;
unsafe {
core::arch::asm!("blr {f}", f = in(reg) resolve::set_count_getter(),
inlateout("x0") set => count,
in("x1") elem_meta,
in("x2") hashable_wt,
lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
lateout("x7") _, lateout("x8") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x20") _, lateout("x21") _, clobber_abi("C"),
);
}
count.max(0) as usize
}
pub unsafe fn copy_value(dest: *mut u8, src: *const u8, metadata: *const c_void) {
unsafe {
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata);
(vwt.initialize_with_copy)(dest as *mut c_void, src as *mut c_void, metadata);
}
}
pub unsafe fn array_parts(array: u64) -> (*const u8, usize) {
unsafe {
let base = array as *const u8;
let count = *(base.add(16) as *const usize);
(base.add(32), count)
}
}
pub unsafe fn generic_key_argument_count(metadata: *const c_void) -> Option<u16> {
if metadata.is_null() {
return None;
}
unsafe {
let words = metadata as *const usize;
let descriptor = if words.read() > 0x7ff {
words.byte_add(64).read()
} else {
words.add(1).read()
} as *const u8;
if descriptor.is_null() {
return None;
}
let flags = (descriptor as *const u32).read();
if flags & 0x80 == 0 {
return None;
}
let base = match flags & 0x1f {
0x10 => 44, 0x11 | 0x12 => 28, _ => return None,
};
let header = descriptor.add(base + 8) as *const u16;
Some(header.add(2).read())
}
}
pub unsafe fn struct_field_offsets(meta: *const c_void) -> Option<Vec<usize>> {
unsafe {
let descriptor = *(meta as *const *const c_void).add(1);
if descriptor.is_null() {
return None;
}
let words = descriptor as *const u32;
let num_fields = *words.add(5) as usize;
let vector_at = *words.add(6) as usize;
if num_fields == 0 || vector_at == 0 {
return None;
}
let vector = (meta as *const u32).add(vector_at * 2);
Some((0..num_fields).map(|i| *vector.add(i) as usize).collect())
}
}
pub unsafe fn mangled_type_name(meta: *const c_void) -> Option<Vec<u8>> {
#[repr(C)]
struct NamePair {
data: *const u8,
length: usize,
}
unsafe extern "C" {
fn swift_getMangledTypeName(meta: *const c_void) -> NamePair;
}
unsafe {
if meta.is_null() {
return None;
}
let NamePair { data: p, length: n } = swift_getMangledTypeName(meta);
if p.is_null() || n == 0 {
return None;
}
let raw = core::slice::from_raw_parts(p, n);
Some(raw.strip_prefix(b"$s").unwrap_or(raw).to_vec())
}
}
pub unsafe fn field_type_metadata(meta: *const c_void, index: usize) -> *const c_void {
unsafe extern "C" {
fn swift_getTypeByMangledNameInContext(
name: *const u8,
length: usize,
context: *const c_void,
generic_args: *const *const c_void,
) -> *const c_void;
}
unsafe {
let descriptor = *(meta as *const *const u8).add(1);
if descriptor.is_null() {
return core::ptr::null();
}
let at = descriptor.add(16);
let rel = at.cast::<i32>().read_unaligned();
if rel == 0 {
return core::ptr::null();
}
let fields = at.offset(rel as isize);
let record_size = fields.add(10).cast::<u16>().read_unaligned() as usize;
let count = fields.add(12).cast::<u32>().read_unaligned() as usize;
if index >= count || record_size == 0 {
return core::ptr::null();
}
let record = fields.add(16 + index * record_size);
let name_at = record.add(4);
let name_rel = name_at.cast::<i32>().read_unaligned();
if name_rel == 0 {
return core::ptr::null();
}
let name = name_at.offset(name_rel as isize);
let mut len = 0usize;
loop {
let b = *name.add(len);
match b {
0 => break,
0x01..=0x17 => len += 5,
_ => len += 1,
}
}
let generic_args = (meta as *const *const c_void).add(2);
swift_getTypeByMangledNameInContext(name, len, descriptor as *const c_void, generic_args)
}
}
pub unsafe fn enum_case_index(meta: *const c_void, case: &str) -> Option<usize> {
unsafe {
let descriptor = *(meta as *const *const u8).add(1);
if descriptor.is_null() {
return None;
}
let at = descriptor.add(16);
let rel = at.cast::<i32>().read_unaligned();
if rel == 0 {
return None;
}
let fields = at.offset(rel as isize);
let record_size = fields.add(10).cast::<u16>().read_unaligned() as usize;
let count = fields.add(12).cast::<u32>().read_unaligned() as usize;
if record_size == 0 {
return None;
}
for i in 0..count {
let record = fields.add(16 + i * record_size);
let name_at = record.add(8);
let name_rel = name_at.cast::<i32>().read_unaligned();
if name_rel == 0 {
continue;
}
let name = name_at.offset(name_rel as isize);
let mut len = 0usize;
while *name.add(len) != 0 {
len += 1;
}
if core::str::from_utf8(core::slice::from_raw_parts(name, len)) == Ok(case) {
return Some(i);
}
}
None
}
}