1use std::{
4 cell::SyncUnsafeCell,
5 ptr::{DynMetadata, Pointee},
6};
7
8pub use async_trait::async_trait;
9pub use bincode;
10pub use ctor;
11pub use inventory;
12use rustc_hash::FxHashMap;
13pub use shrink_to_fit;
14pub use tracing;
15
16#[cfg(debug_assertions)]
17use crate::debug::ValueDebugFormatString;
18use crate::{NonLocalValue, RawVc, TaskInput, TaskPersistence, TraitType, ValueType, ValueTypeId};
19pub use crate::{
20 dyn_task_inputs::DynTaskInputs,
21 global_name_for_method, global_name_for_scope, global_name_for_trait_method,
22 global_name_for_trait_method_impl, global_name_for_type, inventory_submit,
23 manager::{find_cell_by_id, find_cell_by_type, spawn_detached_for_testing},
24 native_function::{
25 ArgMeta, NativeFunction, VTABLE_DEFAULT, downcast_args_owned, downcast_args_ref,
26 downcast_stack_args_owned,
27 },
28 registry::RegistryDef,
29 task::function::{into_task_fn, into_task_fn_with_this},
30 turbo_register,
31 value_type::{TraitVtablePrototype, build_trait_vtable, index_of_method_name},
32};
33
34#[cfg(debug_assertions)]
35#[inline(never)]
36pub async fn value_debug_format_field(value: ValueDebugFormatString<'_>) -> String {
37 match value.try_to_string().await {
38 Ok(result) => result,
39 Err(err) => format!("{err:?}"),
40 }
41}
42
43pub fn get_persistence_from_inputs(inputs: &impl TaskInput) -> TaskPersistence {
44 if inputs.is_transient() {
45 TaskPersistence::Transient
46 } else {
47 TaskPersistence::Persistent
48 }
49}
50
51pub fn get_persistence_from_inputs_and_this(
52 this: RawVc,
53 inputs: &impl TaskInput,
54) -> TaskPersistence {
55 if this.is_transient() || inputs.is_transient() {
56 TaskPersistence::Transient
57 } else {
58 TaskPersistence::Persistent
59 }
60}
61
62pub fn assert_argument_is_non_local_value<Argument: NonLocalValue>() {}
63
64#[macro_export]
65macro_rules! stringify_path {
66 ($path:path) => {
67 stringify!($path)
68 };
69}
70
71#[inline(always)]
74pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
75 std::ptr::metadata(ptr)
77}
78
79#[doc(hidden)]
82pub const fn const_type_name<T: ?Sized>() -> &'static str {
83 std::any::type_name::<T>()
84}
85
86#[doc(hidden)]
88pub const fn const_concat_len(slices: &[&str]) -> usize {
89 let mut total = 0;
90 let mut i = 0;
91 while i < slices.len() {
92 total += slices[i].len();
93 i += 1;
94 }
95 total
96}
97
98#[doc(hidden)]
100pub const fn const_concat_into<const N: usize>(slices: &[&str]) -> [u8; N] {
101 let mut buf = [0u8; N];
102 let mut pos = 0;
103 let mut i = 0;
104 while i < slices.len() {
105 let bytes = slices[i].as_bytes();
106 let (_, rest) = buf.split_at_mut(pos);
107 let (dst, _) = rest.split_at_mut(bytes.len());
108 dst.copy_from_slice(bytes);
109 pos += bytes.len();
110 i += 1;
111 }
112 assert!(pos == N, "const_concat: length mismatch");
113 buf
114}
115
116#[doc(hidden)]
125#[macro_export]
126macro_rules! const_concat {
127 ($slices:expr) => {{
128 const SLICES: &[&str] = $slices;
129 const LEN: usize = $crate::macro_helpers::const_concat_len(SLICES);
130 const BYTES: [u8; LEN] = $crate::macro_helpers::const_concat_into(SLICES);
131 const STR: &str = unsafe { ::std::str::from_utf8_unchecked(&BYTES) };
133 STR
134 }};
135}
136
137#[doc(hidden)]
140pub const fn strip_trailing_segments(s: &str, count: usize) -> &str {
141 let mut remaining = s;
142 let mut i = 0;
143 while i < count {
144 let bytes = remaining.as_bytes();
145 if bytes.len() < 2 {
146 return s;
147 }
148 let mut pos = bytes.len();
149 loop {
150 if pos < 2 {
151 return s;
152 }
153 pos -= 1;
154 if bytes[pos] == b':' && bytes[pos - 1] == b':' {
155 (remaining, _) = remaining.split_at(pos - 1);
156 break;
157 }
158 }
159 i += 1;
160 }
161 remaining
162}
163
164pub struct VTableRegistry<T>
179where
180 T: Pointee<Metadata = DynMetadata<T>> + ?Sized,
181{
182 #[allow(clippy::type_complexity)]
186 pending: SyncUnsafeCell<Option<Vec<(&'static ValueType, DynMetadata<T>)>>>,
187 inner: SyncUnsafeCell<Option<FxHashMap<ValueTypeId, DynMetadata<T>>>>,
189}
190
191unsafe impl<T> Sync for VTableRegistry<T> where T: Pointee<Metadata = DynMetadata<T>> + ?Sized {}
196
197impl<T> VTableRegistry<T>
198where
199 T: Pointee<Metadata = DynMetadata<T>> + ?Sized,
200{
201 pub const fn new() -> Self {
202 Self {
203 pending: SyncUnsafeCell::new(Some(Vec::new())),
204 inner: SyncUnsafeCell::new(None),
205 }
206 }
207
208 pub fn register(&'static self, value_type: &'static ValueType, fat_ptr: *const T) {
216 let pending = unsafe { &mut *self.pending.get() };
218 let Some(pending) = pending.as_mut() else {
219 panic!("VTableRegistry::register called after finalize for {value_type}");
220 };
221 pending.push((value_type, std::ptr::metadata(fat_ptr)));
222 }
223
224 pub fn finalize(&'static self) {
230 let pending = unsafe { &mut *self.pending.get() };
233 let Some(pending) = pending.take() else {
236 return;
237 };
238 let inner = unsafe { &mut *self.inner.get() };
239 debug_assert!(inner.is_none(), "inner already populated");
240 let mut map = FxHashMap::with_capacity_and_hasher(pending.len(), Default::default());
241 for (value_type, metadata) in pending {
242 let id = unsafe { crate::registry::get_value_type_id_unchecked(value_type) };
246 let prev = map.insert(id, metadata);
247 debug_assert!(
248 prev.is_none(),
249 "multiple trait impls registered for {value_type}"
250 );
251 }
252 *inner = Some(map);
253 }
254
255 pub(crate) fn cast(&self, id: ValueTypeId, raw: *const ()) -> *const T {
256 let inner = unsafe { &*self.inner.get() };
261 let Some(metadata) = inner.as_ref().and_then(|map| map.get(&id)) else {
262 panic!(
263 "no trait impl registered for value type {}",
264 crate::registry::get_value_type(id)
265 )
266 };
267 std::ptr::from_raw_parts(raw, *metadata)
268 }
269}
270
271impl<T> Default for VTableRegistry<T>
272where
273 T: Pointee<Metadata = DynMetadata<T>> + ?Sized,
274{
275 fn default() -> Self {
276 Self::new()
277 }
278}
279
280pub struct CollectableTraitMethods {
281 pub value_type: &'static ValueType,
282 pub trait_type: &'static TraitType,
283 pub methods: &'static [&'static NativeFunction],
284 pub finalize_vtable_registry: fn(),
289}
290inventory::collect! {CollectableTraitMethods}
291
292#[doc(hidden)]
297#[macro_export]
298macro_rules! inventory_submit {
299 ($($item:tt)*) => {
300 #[cfg(not(rust_analyzer))]
301 $crate::macro_helpers::inventory_submit_inner! { $($item)* }
302 }
303}
304
305#[doc(hidden)]
307pub use inventory::submit as inventory_submit_inner;
308
309#[doc(hidden)]
315#[macro_export]
316macro_rules! global_name_for_type {
317 ($item:ty) => {
318 $crate::macro_helpers::const_type_name::<$item>()
319 };
320}
321
322#[doc(hidden)]
323#[macro_export]
324macro_rules! global_name_for_method {
325 ($ty:ty, $method:ident) => {
326 $crate::const_concat!(&[
327 $crate::macro_helpers::const_type_name::<$ty>(),
328 "::",
329 ::std::stringify!($method),
330 ])
331 };
332}
333
334#[doc(hidden)]
335#[macro_export]
336macro_rules! global_name_for_trait_method {
337 ($trait:path, $method:ident) => {
338 $crate::const_concat!(&[
339 "<",
340 $crate::macro_helpers::const_type_name::<dyn $trait>(),
341 ">::",
342 ::std::stringify!($method),
343 ])
344 };
345}
346
347#[doc(hidden)]
348#[macro_export]
349macro_rules! global_name_for_trait_method_impl {
350 ($ty:ty, $trait:path, $method:ident) => {
351 $crate::const_concat!(&[
352 "<",
353 $crate::macro_helpers::const_type_name::<$ty>(),
354 " as ",
355 $crate::macro_helpers::const_type_name::<dyn $trait>(),
356 ">::",
357 ::std::stringify!($method),
358 ])
359 };
360}
361
362#[doc(hidden)]
364#[macro_export]
365macro_rules! global_name_for_scope {
366 ($depth:literal, $($item:tt)+) => {{
367 struct PlaceholderMarkerType;
368 $crate::const_concat!(&[
369 $crate::macro_helpers::strip_trailing_segments(
370 $crate::macro_helpers::const_type_name::<PlaceholderMarkerType>(),
371 $depth + 1, ),
373 "::",
374 ::std::stringify!($($item)+),
375 ])
376 }}
377}