use super::*;
use crate::abi;
use core::ffi::c_void;
use crate::mangle;
mangle::result_types! {
pub(crate) TEXT_EDITOR_T = ty("TextEditor");
pub(crate) LINK_T = ty("Link").of(vec![ty("Text")]);
pub(crate) GROUP_T = ty("Group").of(vec![ty("AnyView")]);
pub(crate) CONTROL_GROUP_T = ty("ControlGroup").of(vec![ty("AnyView")]);
pub(crate) VIEW_THAT_FITS_T = ty("ViewThatFits").of(vec![ty("AnyView")]);
}
mangle::modifier_types! {
pub(crate) PADDING_T = ty("_PaddingLayout");
pub(crate) OPACITY_T = ty("_OpacityEffect");
pub(crate) FRAME_T = ty("_FrameLayout");
pub(crate) BG_T = ty("_BackgroundStyleModifier").of(vec![ty("Color")]);
pub(crate) CORNER_T = ty("_ClipEffect").of(vec![ty("RoundedRectangle")]);
pub(crate) BORDER_T = ty("_OverlayModifier").of(vec![ty("_ShapeView").of(vec![ty("_StrokedShape").of(vec![Type::Nominal { module: "SwiftUI", path: vec!["Rectangle", "_Inset"], kind: Kind::Struct }]), ty("Color")])]);
pub(crate) FONT_T = ty("_EnvironmentKeyWritingModifier").of(vec![opt(ty("Font"))]);
pub(crate) FOREGROUND_T = ty("_ForegroundStyleModifier").of(vec![ty("Color")]);
pub(crate) FOREGROUND_HIERARCHICAL_T = ty("_ForegroundStyleModifier").of(vec![ty("HierarchicalShapeStyle")]);
pub(crate) FOREGROUND_GRADIENT_T = ty("_ForegroundStyleModifier").of(vec![ty("LinearGradient")]);
pub(crate) FOREGROUND2_T = ty("_ForegroundStyleModifier2").of(vec![ty("Color"), ty("Color")]);
pub(crate) FOREGROUND3_T =
ty("_ForegroundStyleModifier3").of(vec![ty("Color"), ty("Color"), ty("Color")]);
pub(crate) BOLD_T = ty("_EnvironmentKeyTransformModifier").of(vec![Type::Verbatim("Sa".into()).of(vec![ty_class("AnyFontModifier")])]);
}
pub(crate) struct CachedType {
mangled: std::sync::LazyLock<Vec<u8>>,
meta: std::sync::atomic::AtomicUsize,
wt: std::sync::atomic::AtomicUsize,
size: std::sync::atomic::AtomicUsize,
}
impl CachedType {
pub(crate) const fn new(mangled: std::sync::LazyLock<Vec<u8>>) -> Self {
use std::sync::atomic::AtomicUsize;
CachedType {
mangled,
meta: AtomicUsize::new(0),
wt: AtomicUsize::new(0),
size: AtomicUsize::new(0),
}
}
pub(crate) fn bytes(&self) -> &[u8] {
&self.mangled
}
}
impl std::ops::Deref for CachedType {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.mangled
}
}
pub(crate) type Resolved = (usize, usize, usize);
pub struct ModifierType {
mangled: std::sync::LazyLock<Vec<u8>>,
modifier_meta: std::sync::atomic::AtomicUsize,
composed: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<usize, Resolved>>>,
composed_with:
std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<(usize, usize), Resolved>>>,
last: std::sync::atomic::AtomicPtr<Composed>,
}
pub(crate) struct Composed {
recv: usize,
meta: usize,
wt: usize,
size: usize,
}
impl ModifierType {
pub(crate) const fn new(mangled: std::sync::LazyLock<Vec<u8>>) -> Self {
ModifierType {
mangled,
modifier_meta: std::sync::atomic::AtomicUsize::new(0),
composed: std::sync::OnceLock::new(),
composed_with: std::sync::OnceLock::new(),
last: std::sync::atomic::AtomicPtr::new(core::ptr::null_mut()),
}
}
fn remember(&self, recv: usize, meta: usize, wt: usize, size: usize) {
use std::sync::atomic::Ordering;
let entry = Box::into_raw(Box::new(Composed { recv, meta, wt, size }));
self.last.store(entry, Ordering::Release);
}
pub(crate) fn applied_to_content(
&self,
recv_meta: *const c_void,
content_meta: *const c_void,
content_wt: *const c_void,
) -> (*const c_void, *const c_void, usize) {
let map = self
.composed_with
.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
let key = (recv_meta as usize, content_meta as usize);
if let Ok(m) = map.lock() {
if let Some(&(a, b, n)) = m.get(&key) {
return (a as *const c_void, b as *const c_void, n);
}
}
unsafe {
let modifier =
abi::generic_metadata_from(self.modifier_meta(), &[content_meta, content_wt]);
assert!(
!modifier.is_null(),
"{} over {}",
crate::diag::demangle_type(&self.mangled),
crate::diag::type_name(content_meta)
);
let meta = abi::generic_metadata2(resolve::modified_content_ma(), recv_meta, modifier);
assert!(
!meta.is_null(),
"ModifiedContent<{}, {}>",
crate::diag::type_name(recv_meta),
crate::diag::type_name(modifier)
);
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let n = abi::type_bytes(meta);
if let Ok(mut m) = map.lock() {
m.insert(key, (meta as usize, wt as usize, n));
}
(meta, wt, n)
}
}
fn modifier_meta(&self) -> *const c_void {
use std::sync::atomic::Ordering;
let hit = self.modifier_meta.load(Ordering::Acquire);
if hit != 0 {
return hit as *const c_void;
}
let m = unsafe { abi::resolve_type_by_mangled_name(&self.mangled) };
assert!(!m.is_null(), "Failed to resolve modifier: {}",
crate::diag::demangle_type(&self.mangled));
self.modifier_meta.store(m as usize, Ordering::Release);
m
}
pub(crate) fn applied_to(&self, recv_meta: *const c_void) -> (*const c_void, *const c_void, usize) {
use std::sync::atomic::Ordering;
let hit = self.last.load(Ordering::Acquire);
if !hit.is_null() {
let e = unsafe { &*hit };
if e.recv == recv_meta as usize {
return (e.meta as *const c_void, e.wt as *const c_void, e.size);
}
}
let map = self.composed.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
if let Ok(m) = map.lock() {
if let Some(&(a, b, n)) = m.get(&(recv_meta as usize)) {
self.remember(recv_meta as usize, a, b, n);
return (a as *const c_void, b as *const c_void, n);
}
}
unsafe {
let meta = abi::generic_metadata2(
resolve::modified_content_ma(),
recv_meta,
self.modifier_meta(),
);
assert!(!meta.is_null(), "ModifiedContent<{}, {}>",
crate::diag::type_name(recv_meta),
crate::diag::demangle_type(&self.mangled));
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let n = abi::type_bytes(meta);
if let Ok(mut m) = map.lock() {
m.insert(recv_meta as usize, (meta as usize, wt as usize, n));
}
self.remember(recv_meta as usize, meta as usize, wt as usize, n);
(meta, wt, n)
}
}
}
pub(crate) trait ResolvesToType {
unsafe fn resolve(self) -> (*const c_void, *const c_void, usize);
}
impl ResolvesToType for &'static CachedType {
unsafe fn resolve(self) -> (*const c_void, *const c_void, usize) {
use std::sync::atomic::Ordering;
let meta = self.meta.load(Ordering::Acquire);
if meta != 0 {
return (
meta as *const c_void,
self.wt.load(Ordering::Relaxed) as *const c_void,
self.size.load(Ordering::Relaxed),
);
}
let (m, w, n) = unsafe { resolve_uncached(&self.mangled) };
self.wt.store(w as usize, Ordering::Relaxed);
self.size.store(n, Ordering::Relaxed);
self.meta.store(m as usize, Ordering::Release);
(m, w, n)
}
}
impl ResolvesToType for &[u8] {
unsafe fn resolve(self) -> (*const c_void, *const c_void, usize) {
unsafe { result_type_slow(self) }
}
}
pub(crate) unsafe fn result_type(
key: impl ResolvesToType,
) -> (*const c_void, *const c_void, usize) {
unsafe { key.resolve() }
}
unsafe fn result_type_slow(mangled: &[u8]) -> (*const c_void, *const c_void, usize) {
use std::collections::HashMap;
use std::sync::{Mutex, OnceLock};
static CACHE: OnceLock<Mutex<HashMap<Vec<u8>, Resolved>>> = OnceLock::new();
let cache = CACHE.get_or_init(|| Mutex::new(HashMap::new()));
if let Ok(map) = cache.lock() {
if let Some(&(m, w, n)) = map.get(mangled) {
return (m as *const c_void, w as *const c_void, n);
}
}
let (meta, wt, n) = unsafe { resolve_uncached(mangled) };
if let Ok(mut map) = cache.lock() {
map.insert(mangled.to_vec(), (meta as usize, wt as usize, n));
}
(meta, wt, n)
}
unsafe fn resolve_uncached(mangled: &[u8]) -> (*const c_void, *const c_void, usize) {
let meta = unsafe { abi::resolve_type_by_mangled_name(mangled) };
assert!(!meta.is_null(), "Failed to resolve: {}", crate::diag::demangle_type(mangled));
let wt = unsafe { abi::get_view_wt(meta) };
assert!(!wt.is_null(), "{} !: View", crate::diag::demangle_type(mangled));
let n = unsafe { abi::type_bytes(meta) };
(meta, wt, n)
}
pub(crate) unsafe fn wrap_modified(
result: Vec<u8>,
modifier: &ModifierType,
recv: &ViewHandle,
) -> ViewHandle {
let (meta, wt, n) = modifier.applied_to(recv.meta);
assert!(
result.len() >= n,
"{} needs {n} bytes, buffer was {} (receiver {})",
crate::diag::type_name(meta), result.len(), crate::diag::type_name(recv.meta),
);
unsafe { ViewHandle::adopt(result, meta, wt) }
}
#[doc(hidden)]
pub fn bench_result_type_paths(iters: u32) -> (f64, f64) {
use std::time::Instant;
let cached = || unsafe { result_type(&GROUP_T) };
let mapped = || unsafe { result_type(GROUP_T.bytes()) };
std::hint::black_box(cached());
std::hint::black_box(mapped());
let mut best = (f64::MAX, f64::MAX);
for _ in 0..9 {
let t = Instant::now();
for _ in 0..iters {
std::hint::black_box(cached());
}
let a = t.elapsed().as_nanos() as f64 / f64::from(iters);
let t = Instant::now();
for _ in 0..iters {
std::hint::black_box(mapped());
}
let b = t.elapsed().as_nanos() as f64 / f64::from(iters);
best = (best.0.min(a), best.1.min(b));
}
best
}