turbo_tasks/
value_type.rs

1use std::{
2    fmt::{self, Debug, Display, Formatter},
3    hash::Hash,
4};
5
6use auto_hash_map::{AutoMap, AutoSet};
7use bincode::{Decode, Encode};
8use tracing::Span;
9use turbo_bincode::{AnyDecodeFn, AnyEncodeFn};
10
11use crate::{
12    RawVc, SharedReference, VcValueType, id::TraitTypeId, macro_helpers::NativeFunction,
13    magic_any::any_as_encode, registry, task::shared_reference::TypedSharedReference,
14    vc::VcCellMode,
15};
16
17type RawCellFactoryFn = fn(TypedSharedReference) -> RawVc;
18
19// TODO this type need some refactoring when multiple languages are added to
20// turbo-task In this case a trait_method might be of a different function type.
21// It probably need to be a Vc<Function>.
22// That's also needed in a distributed world, where the function might be only
23// available on a remote instance.
24
25/// A definition of a type of data.
26///
27/// Contains a list of traits and trait methods that are available on that type.
28pub struct ValueType {
29    /// A readable name of the type
30    pub name: &'static str,
31    /// The fully qualitifed global name of the type.
32    pub global_name: &'static str,
33    /// Set of traits available
34    traits: AutoSet<TraitTypeId>,
35    /// List of trait methods available
36    trait_methods: AutoMap<&'static TraitMethod, &'static NativeFunction>,
37
38    /// Functions to convert to write the type to a buffer or read it from a buffer.
39    pub bincode: Option<(AnyEncodeFn, AnyDecodeFn<SharedReference>)>,
40
41    /// An implementation of
42    /// [`VcCellMode::raw_cell`][crate::vc::VcCellMode::raw_cell].
43    ///
44    /// Allows dynamically constructing a cell using the type id. Used inside of
45    /// [`TraitRef`][crate::TraitRef] where we have a type id, but not the concrete type `T` of
46    /// `Vc<T>`.
47    ///
48    /// Because we allow resolving `Vc<dyn Trait>`, it's otherwise not possible
49    /// for `RawVc` to know what the appropriate `VcCellMode` is.
50    pub(crate) raw_cell: RawCellFactoryFn,
51}
52
53impl Hash for ValueType {
54    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
55        (self as *const ValueType).hash(state);
56    }
57}
58
59impl Eq for ValueType {}
60
61impl PartialEq for ValueType {
62    fn eq(&self, other: &Self) -> bool {
63        std::ptr::eq(self, other)
64    }
65}
66
67impl Debug for ValueType {
68    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
69        let mut d = f.debug_struct("ValueType");
70        d.field("name", &self.name);
71        for trait_id in self.traits.iter() {
72            for (name, m) in &registry::get_trait(*trait_id).methods {
73                if self.trait_methods.contains_key(&m) {
74                    d.field(name, &"(trait fn)");
75                }
76            }
77        }
78        d.finish()
79    }
80}
81
82impl Display for ValueType {
83    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
84        f.write_str(self.name)
85    }
86}
87
88pub trait ManualEncodeWrapper: Encode {
89    type Value;
90
91    // this uses RPIT to avoid some lifetime problems
92    fn new<'a>(value: &'a Self::Value) -> impl Encode + 'a;
93}
94
95pub trait ManualDecodeWrapper: Decode<()> {
96    type Value;
97
98    fn inner(self) -> Self::Value;
99}
100
101impl ValueType {
102    /// This is internally used by [`#[turbo_tasks::value]`][crate::value].
103    pub fn new<T: VcValueType>(global_name: &'static str) -> Self {
104        Self::new_inner::<T>(global_name, None)
105    }
106
107    /// This is internally used by [`#[turbo_tasks::value]`][crate::value].
108    pub fn new_with_bincode<T: VcValueType + Encode + Decode<()>>(
109        global_name: &'static str,
110    ) -> Self {
111        Self::new_inner::<T>(
112            global_name,
113            Some((
114                |this, enc| {
115                    T::encode(any_as_encode::<T>(this), enc)?;
116                    Ok(())
117                },
118                |dec| {
119                    let val = T::decode(dec)?;
120                    Ok(SharedReference::new(triomphe::Arc::new(val)))
121                },
122            )),
123        )
124    }
125
126    /// This is used internally by [`turbo_tasks_macros::primitive`] to encode/decode foreign types
127    /// that cannot implement the [`bincode`] traits due to the [orphan rules].
128    ///
129    /// This is done by constructing wrapper types that implement the bincode traits on behalf of
130    /// the wrapped type.
131    ///
132    /// [orphan rules]: https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules
133    pub fn new_with_bincode_wrappers<
134        T: VcValueType,
135        E: ManualEncodeWrapper<Value = T>,
136        D: ManualDecodeWrapper<Value = T>,
137    >(
138        global_name: &'static str,
139    ) -> Self {
140        Self::new_inner::<T>(
141            global_name,
142            Some((
143                |this, enc| {
144                    E::new(any_as_encode::<T>(this)).encode(enc)?;
145                    Ok(())
146                },
147                |dec| {
148                    let val = D::inner(D::decode(dec)?);
149                    Ok(SharedReference::new(triomphe::Arc::new(val)))
150                },
151            )),
152        )
153    }
154
155    // Helper for other constructor functions
156    fn new_inner<T: VcValueType>(
157        global_name: &'static str,
158        bincode: Option<(AnyEncodeFn, AnyDecodeFn<SharedReference>)>,
159    ) -> Self {
160        Self {
161            name: std::any::type_name::<T>(),
162            global_name,
163            traits: AutoSet::new(),
164            trait_methods: AutoMap::new(),
165            bincode,
166            raw_cell: <T::CellMode as VcCellMode<T>>::raw_cell,
167        }
168    }
169
170    pub(crate) fn register_trait_method(
171        &mut self,
172        trait_method: &'static TraitMethod,
173        native_fn: &'static NativeFunction,
174    ) {
175        self.trait_methods.insert(trait_method, native_fn);
176    }
177
178    pub fn get_trait_method(
179        &self,
180        trait_method: &'static TraitMethod,
181    ) -> Option<&'static NativeFunction> {
182        match self.trait_methods.get(trait_method) {
183            Some(f) => Some(*f),
184            None => trait_method.default_method,
185        }
186    }
187
188    pub(crate) fn register_trait(&mut self, trait_type: TraitTypeId) {
189        self.traits.insert(trait_type);
190    }
191
192    pub fn has_trait(&self, trait_type: &TraitTypeId) -> bool {
193        self.traits.contains(trait_type)
194    }
195
196    pub fn traits_iter(&self) -> impl Iterator<Item = TraitTypeId> + '_ {
197        self.traits.iter().cloned()
198    }
199}
200
201// A collectable struct for value types
202pub struct CollectableValueType(pub &'static once_cell::sync::Lazy<ValueType>);
203
204inventory::collect! {CollectableValueType}
205
206pub struct TraitMethod {
207    pub(crate) trait_name: &'static str,
208    pub(crate) method_name: &'static str,
209    pub(crate) default_method: Option<&'static NativeFunction>,
210}
211impl Hash for TraitMethod {
212    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
213        (self as *const TraitMethod).hash(state);
214    }
215}
216
217impl Eq for TraitMethod {}
218
219impl PartialEq for TraitMethod {
220    fn eq(&self, other: &Self) -> bool {
221        std::ptr::eq(self, other)
222    }
223}
224impl Debug for TraitMethod {
225    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
226        f.debug_struct("TraitMethod")
227            .field("trait_name", &self.trait_name)
228            .field("name", &self.method_name)
229            .field("default_method", &self.default_method)
230            .finish()
231    }
232}
233impl TraitMethod {
234    pub(crate) fn resolve_span(&self) -> Span {
235        tracing::trace_span!(
236            "turbo_tasks::resolve_trait_call",
237            name = format_args!("{}::{}", &self.trait_name, &self.method_name),
238        )
239    }
240}
241
242#[derive(Debug)]
243pub struct TraitType {
244    pub name: &'static str,
245    pub global_name: &'static str,
246    pub(crate) methods: AutoMap<&'static str, TraitMethod>,
247}
248
249impl Hash for TraitType {
250    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
251        (self as *const TraitType).hash(state);
252    }
253}
254
255impl Display for TraitType {
256    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
257        write!(f, "trait {}", self.name)
258    }
259}
260
261impl Eq for TraitType {}
262
263impl PartialEq for TraitType {
264    fn eq(&self, other: &Self) -> bool {
265        std::ptr::eq(self, other)
266    }
267}
268
269impl TraitType {
270    pub fn new(
271        name: &'static str,
272        global_name: &'static str,
273        trait_methods: Vec<(&'static str, Option<&'static NativeFunction>)>,
274    ) -> Self {
275        let mut methods = AutoMap::new();
276        for (method_name, default_method) in trait_methods {
277            let prev = methods.insert(
278                method_name,
279                TraitMethod {
280                    trait_name: name,
281                    method_name,
282                    default_method,
283                },
284            );
285            debug_assert!(
286                prev.is_none(),
287                "duplicate methods {method_name} registered on {global_name}"
288            );
289        }
290        Self {
291            name,
292            global_name,
293            methods,
294        }
295    }
296
297    pub fn get(&self, name: &str) -> &TraitMethod {
298        self.methods.get(name).unwrap()
299    }
300}
301
302pub struct CollectableTrait(pub &'static once_cell::sync::Lazy<TraitType>);
303
304inventory::collect! {CollectableTrait}