1use std::{
4 cell::SyncUnsafeCell,
5 ptr::{DynMetadata, Pointee},
6};
7
8pub use async_trait::async_trait;
9pub use bincode;
10use rustc_hash::FxHashMap;
11pub use scattered_collect;
12use scattered_collect::slice::ScatteredSlice;
13pub use shrink_to_fit;
14pub use tracing;
15
16#[cfg(debug_assertions)]
17use crate::debug::ValueDebugFormatString;
18use crate::{
19 InputResolution, NonLocalValue, RawVc, TaskInput, TaskPersistence, TraitType, ValueType,
20 ValueTypeId,
21};
22pub use crate::{
23 dyn_task_inputs::DynTaskInputs,
24 global_name_for_method, global_name_for_scope, global_name_for_trait_method,
25 global_name_for_trait_method_impl, global_name_for_type,
26 manager::{find_cell_by_id, find_cell_by_type, spawn_detached_for_testing},
27 native_function::{
28 ArgMeta, NativeFunction, VTABLE_DEFAULT, downcast_args_owned, downcast_args_ref,
29 downcast_stack_args_owned,
30 },
31 register_function, register_trait, register_value,
32 registry::RegistryDef,
33 task::function::{into_task_fn, into_task_fn_with_this},
34 value_type::{TraitVtablePrototype, build_trait_vtable, index_of_method_name},
35};
36
37#[cfg(debug_assertions)]
38#[inline(never)]
39pub async fn value_debug_format_field(value: ValueDebugFormatString<'_>) -> String {
40 match value.try_to_string().await {
41 Ok(result) => result,
42 Err(err) => format!("{err:?}"),
43 }
44}
45
46pub fn get_persistence_from_inputs(inputs: &impl TaskInput) -> TaskPersistence {
47 if inputs.is_transient() {
48 TaskPersistence::Transient
49 } else {
50 TaskPersistence::Persistent
51 }
52}
53
54#[inline(always)]
59pub fn input_resolution(inputs: &impl TaskInput) -> InputResolution {
60 InputResolution::from_is_resolved(inputs.is_resolved())
61}
62
63pub fn get_persistence_from_inputs_and_this(
64 this: RawVc,
65 inputs: &impl TaskInput,
66) -> TaskPersistence {
67 if this.is_transient() || inputs.is_transient() {
68 TaskPersistence::Transient
69 } else {
70 TaskPersistence::Persistent
71 }
72}
73
74pub fn assert_argument_is_non_local_value<Argument: NonLocalValue>() {}
75
76#[macro_export]
77macro_rules! stringify_path {
78 ($path:path) => {
79 stringify!($path)
80 };
81}
82
83#[inline(always)]
86pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
87 std::ptr::metadata(ptr)
89}
90
91#[doc(hidden)]
94pub const fn const_type_name<T: ?Sized>() -> &'static str {
95 std::any::type_name::<T>()
96}
97
98#[doc(hidden)]
100pub const fn const_concat_len(slices: &[&str]) -> usize {
101 let mut total = 0;
102 let mut i = 0;
103 while i < slices.len() {
104 total += slices[i].len();
105 i += 1;
106 }
107 total
108}
109
110#[doc(hidden)]
112pub const fn const_concat_into<const N: usize>(slices: &[&str]) -> [u8; N] {
113 let mut buf = [0u8; N];
114 let mut pos = 0;
115 let mut i = 0;
116 while i < slices.len() {
117 let bytes = slices[i].as_bytes();
118 let (_, rest) = buf.split_at_mut(pos);
119 let (dst, _) = rest.split_at_mut(bytes.len());
120 dst.copy_from_slice(bytes);
121 pos += bytes.len();
122 i += 1;
123 }
124 assert!(pos == N, "const_concat: length mismatch");
125 buf
126}
127
128#[doc(hidden)]
137#[macro_export]
138macro_rules! const_concat {
139 ($slices:expr) => {{
140 const SLICES: &[&str] = $slices;
141 const LEN: usize = $crate::macro_helpers::const_concat_len(SLICES);
142 const BYTES: [u8; LEN] = $crate::macro_helpers::const_concat_into(SLICES);
143 const STR: &str = unsafe { ::std::str::from_utf8_unchecked(&BYTES) };
145 STR
146 }};
147}
148
149#[doc(hidden)]
152pub const fn strip_trailing_segments(s: &str, count: usize) -> &str {
153 let mut remaining = s;
154 let mut i = 0;
155 while i < count {
156 let bytes = remaining.as_bytes();
157 if bytes.len() < 2 {
158 return s;
159 }
160 let mut pos = bytes.len();
161 loop {
162 if pos < 2 {
163 return s;
164 }
165 pos -= 1;
166 if bytes[pos] == b':' && bytes[pos - 1] == b':' {
167 (remaining, _) = remaining.split_at(pos - 1);
168 break;
169 }
170 }
171 i += 1;
172 }
173 remaining
174}
175
176pub struct VTableRegistry<T>
178where
179 T: Pointee<Metadata = DynMetadata<T>> + ?Sized,
180{
181 inner: SyncUnsafeCell<Option<FxHashMap<ValueTypeId, DynMetadata<T>>>>,
184}
185
186unsafe impl<T> Sync for VTableRegistry<T> where T: Pointee<Metadata = DynMetadata<T>> + ?Sized {}
191
192impl<T> VTableRegistry<T>
193where
194 T: Pointee<Metadata = DynMetadata<T>> + ?Sized,
195{
196 pub const fn new() -> Self {
197 Self {
198 inner: SyncUnsafeCell::new(None),
199 }
200 }
201
202 pub fn insert(&'static self, id: ValueTypeId, metadata: DynMetadata<T>) {
210 let inner = unsafe { &mut *self.inner.get() };
213 let map = inner.get_or_insert_with(FxHashMap::default);
214 let prev = map.insert(id, metadata);
215 debug_assert!(
216 prev.is_none(),
217 "multiple trait impls registered for value type id {id}"
218 );
219 }
220
221 pub(crate) fn cast(&self, id: ValueTypeId, raw: *const ()) -> *const T {
222 let inner = unsafe { &*self.inner.get() };
227 let Some(metadata) = inner.as_ref().and_then(|map| map.get(&id)) else {
228 panic!(
229 "no trait impl registered for value type {}",
230 crate::registry::get_value_type(id)
231 )
232 };
233 std::ptr::from_raw_parts(raw, *metadata)
234 }
235}
236
237impl<T> Default for VTableRegistry<T>
238where
239 T: Pointee<Metadata = DynMetadata<T>> + ?Sized,
240{
241 fn default() -> Self {
242 Self::new()
243 }
244}
245
246pub struct TraitImplRecord {
249 pub value_type: &'static ValueType,
250 pub trait_type: &'static TraitType,
251 pub methods: &'static [&'static NativeFunction],
252 pub install_vtable: fn(ValueTypeId),
255}
256
257#[doc(hidden)]
264#[scattered_collect::gather]
265pub static TRAIT_IMPLS_SLICE: ScatteredSlice<TraitImplRecord>;
266
267#[doc(hidden)]
273#[macro_export]
274macro_rules! global_name_for_type {
275 ($item:ty) => {
276 $crate::macro_helpers::const_type_name::<$item>()
277 };
278}
279
280#[doc(hidden)]
281#[macro_export]
282macro_rules! global_name_for_method {
283 ($ty:ty, $method:ident) => {
284 $crate::const_concat!(&[
285 $crate::macro_helpers::const_type_name::<$ty>(),
286 "::",
287 ::std::stringify!($method),
288 ])
289 };
290}
291
292#[doc(hidden)]
293#[macro_export]
294macro_rules! global_name_for_trait_method {
295 ($trait:path, $method:ident) => {
296 $crate::const_concat!(&[
297 "<",
298 $crate::macro_helpers::const_type_name::<dyn $trait>(),
299 ">::",
300 ::std::stringify!($method),
301 ])
302 };
303}
304
305#[doc(hidden)]
306#[macro_export]
307macro_rules! global_name_for_trait_method_impl {
308 ($ty:ty, $trait:path, $method:ident) => {
309 $crate::const_concat!(&[
310 "<",
311 $crate::macro_helpers::const_type_name::<$ty>(),
312 " as ",
313 $crate::macro_helpers::const_type_name::<dyn $trait>(),
314 ">::",
315 ::std::stringify!($method),
316 ])
317 };
318}
319
320#[doc(hidden)]
322#[macro_export]
323macro_rules! global_name_for_scope {
324 ($depth:literal, $($item:tt)+) => {{
325 struct PlaceholderMarkerType;
326 $crate::const_concat!(&[
327 $crate::macro_helpers::strip_trailing_segments(
328 $crate::macro_helpers::const_type_name::<PlaceholderMarkerType>(),
329 $depth + 1, ),
331 "::",
332 ::std::stringify!($($item)+),
333 ])
334 }}
335}