tor_rpcbase/
obj.rs

1//! Object type for our RPC system.
2
3pub(crate) mod cast;
4
5use std::sync::Arc;
6
7use derive_deftly::define_derive_deftly;
8use downcast_rs::DowncastSync;
9use serde::{Deserialize, Serialize};
10
11use self::cast::CastTable;
12
13/// An object in our RPC system to which methods can be addressed.
14///
15/// You shouldn't implement this trait yourself; instead, use the
16/// [`derive_deftly(Object)`].
17///
18/// See the documentation for [`derive_deftly(Object)`]
19/// for examples of how to declare and
20/// downcast `Object`s.
21///
22/// [`derive_deftly(Object)`]: crate::templates::derive_deftly_template_Object
23pub trait Object: DowncastSync + Send + Sync + 'static {
24    /// Return true if this object should be given an identifier that allows it
25    /// to be used outside of the session that generated it.
26    ///
27    /// Currently, the only use for such IDs in arti is identifying stream
28    /// contexts in when opening a SOCKS connection: When an application opens a
29    /// stream, it needs to declare what RPC context (like a `TorClient`) it's
30    /// using, which requires that some identifier for that context exist
31    /// outside of the RPC session that owns it.
32    fn expose_outside_of_session(&self) -> bool {
33        false
34    }
35
36    /// Return a [`CastTable`] that can be used to downcast a `dyn Object` of
37    /// this type into various kinds of `dyn Trait` references.
38    ///
39    /// The default implementation of this method declares that the `Object`
40    /// can't be downcast into any traits.
41    ///
42    /// You should not implement this method yourself; instead use
43    /// [`derive_deftly(Object)`](crate::templates::derive_deftly_template_Object).
44    fn get_cast_table(&self) -> &CastTable {
45        &cast::EMPTY_CAST_TABLE
46    }
47
48    /// Optionally, return a delegation target for this `Object``.
49    ///
50    /// If method lookup fails on this object, then the `delegate`
51    fn delegate(&self) -> Option<Arc<dyn Object>> {
52        None
53    }
54}
55downcast_rs::impl_downcast!(sync Object);
56
57/// An identifier for an Object within the context of a Session.
58///
59/// These are opaque from the client's perspective.
60#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize)]
61#[serde(transparent)]
62pub struct ObjectId(
63    // (We use Box<str> to save a word here, since these don't have to be
64    // mutable ever.)
65    Box<str>,
66);
67
68impl AsRef<str> for ObjectId {
69    fn as_ref(&self) -> &str {
70        self.0.as_ref()
71    }
72}
73
74impl<T> From<T> for ObjectId
75where
76    T: Into<Box<str>>,
77{
78    fn from(value: T) -> Self {
79        Self(value.into())
80    }
81}
82
83/// Extension trait for `Arc<dyn Object>` to support convenient
84/// downcasting to `dyn Trait`.
85///
86/// You don't need to use this for downcasting to an object's concrete
87/// type; for that, use [`downcast_rs::DowncastSync`].
88///
89/// # Examples
90///
91/// ```
92/// use tor_rpcbase::{Object, ObjectArcExt, templates::*};
93/// use derive_deftly::Deftly;
94/// use std::sync::Arc;
95///
96/// #[derive(Deftly)]
97/// #[derive_deftly(Object)]
98/// #[deftly(rpc(downcastable_to = "HasFeet"))]
99/// pub struct Frog {}
100/// pub trait HasFeet {
101///     fn num_feet(&self) -> usize;
102/// }
103/// impl HasFeet for Frog {
104///     fn num_feet(&self) -> usize { 4 }
105/// }
106///
107/// /// If `obj` is a HasFeet, return how many feet it has.
108/// /// Otherwise, return 0.
109/// fn check_feet(obj: Arc<dyn Object>) -> usize {
110///     let maybe_has_feet: Option<&dyn HasFeet> = obj.cast_to_trait();
111///     match maybe_has_feet {
112///         Some(foot_haver) => foot_haver.num_feet(),
113///         None => 0,
114///     }
115/// }
116///
117/// assert_eq!(check_feet(Arc::new(Frog{})), 4);
118/// ```
119pub trait ObjectArcExt {
120    /// Try to cast this `Arc<dyn Object>` to a `T`.  On success, return a reference to
121    /// T; on failure, return None.
122    fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T>;
123
124    /// Try to cast this `Arc<dyn Object>` to an `Arc<T>`.
125    fn cast_to_arc_trait<T: ?Sized + 'static>(self) -> Result<Arc<T>, Arc<dyn Object>>;
126}
127
128impl dyn Object {
129    /// Try to cast this `Object` to a `T`.  On success, return a reference to
130    /// T; on failure, return None.
131    ///
132    /// This method is only for casting to `&dyn Trait`;
133    /// see [`ObjectArcExt`] for limitations.
134    pub fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T> {
135        let table = self.get_cast_table();
136        table.cast_object_to(self)
137    }
138}
139
140impl ObjectArcExt for Arc<dyn Object> {
141    fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T> {
142        let obj: &dyn Object = self.as_ref();
143        obj.cast_to_trait()
144    }
145    fn cast_to_arc_trait<T: ?Sized + 'static>(self) -> Result<Arc<T>, Arc<dyn Object>> {
146        let table = self.get_cast_table();
147        table.cast_object_to_arc(self.clone())
148    }
149}
150
151define_derive_deftly! {
152/// Allow a type to participate as an Object in the RPC system.
153///
154/// This template implements `Object` for the
155/// target type, and can be used to cause objects to participate in the trait
156/// downcasting system.
157///
158/// # Examples
159///
160/// ## Simple case, just implements `Object`.
161///
162/// ```
163/// use tor_rpcbase::{self as rpc, templates::*};
164/// use derive_deftly::Deftly;
165///
166/// #[derive(Default, Deftly)]
167/// #[derive_deftly(Object)]
168/// struct Houseplant {
169///    oxygen_per_sec: f64,
170///    benign_neglect: u8
171/// }
172///
173/// // You can downcast an Object to a concrete type.
174/// use downcast_rs::DowncastSync;
175/// use std::sync::Arc;
176/// let plant_obj: Arc<dyn rpc::Object> = Arc::new(Houseplant::default());
177/// let as_plant: Arc<Houseplant> = plant_obj.downcast_arc().ok().unwrap();
178/// ```
179///
180/// ## With trait downcasting
181///
182/// By default, you can use [`downcast_rs`] to downcast a `dyn Object` to its
183/// concrete type.  If you also need to be able to downcast a `dyn Object` to a given
184/// trait that it implements, you can use the `downcastable_to` attributes for `Object` to have
185/// it participate in trait downcasting:
186///
187/// ```
188/// use tor_rpcbase::{self as rpc, templates::*};
189/// use derive_deftly::Deftly;
190///
191/// #[derive(Deftly)]
192/// #[derive_deftly(Object)]
193/// #[deftly(rpc(downcastable_to = "Gizmo, Doodad"))]
194/// struct Frobnitz {}
195///
196/// trait Gizmo {}
197/// trait Doodad {}
198/// impl Gizmo for Frobnitz {}
199/// impl Doodad for Frobnitz {}
200///
201/// use std::sync::Arc;
202/// use rpc::ObjectArcExt; // for the cast_to method.
203/// let frob_obj: Arc<dyn rpc::Object> = Arc::new(Frobnitz {});
204/// let gizmo: &dyn Gizmo = frob_obj.cast_to_trait().unwrap();
205/// let doodad: &dyn Doodad = frob_obj.cast_to_trait().unwrap();
206/// ```
207///
208/// ## With generic objects
209///
210/// Right now, a generic object can't participate in our method lookup system,
211/// but it _can_ participate in trait downcasting.  We'll try to remove this
212/// limitation in the future.
213///
214/// ```
215/// use tor_rpcbase::{self as rpc, templates::*};
216/// use derive_deftly::Deftly;
217///
218/// #[derive(Deftly)]
219/// #[derive_deftly(Object)]
220/// #[deftly(rpc(downcastable_to = "ExampleTrait"))]
221/// struct Generic<T,U> where T:Clone, U:PartialEq {
222///     t: T,
223///     u: U,
224/// }
225///
226/// trait ExampleTrait {}
227/// impl<T:Clone,U:PartialEq> ExampleTrait for Generic<T,U> {}
228///
229/// use std::sync::Arc;
230/// use rpc::ObjectArcExt; // for the cast_to method.
231/// let obj: Arc<dyn rpc::Object> = Arc::new(Generic { t: 42_u8, u: 42_u8 });
232/// let tr: &dyn ExampleTrait = obj.cast_to_trait().unwrap();
233/// ```
234///
235/// ## Making an object "exposed outside of the session"
236///
237/// You can flag any kind of Object so that its identifiers will be exported
238/// outside of the local RPC session.  (Arti uses this for Objects whose
239/// ObjectId needs to be used as a SOCKS identifier.)  To do so,
240/// use the `expose_outside_session` attribute:
241///
242/// ```
243/// use tor_rpcbase::{self as rpc, templates::*};
244/// use derive_deftly::Deftly;
245///
246/// #[derive(Deftly)]
247/// #[derive_deftly(Object)]
248/// #[deftly(rpc(expose_outside_of_session))]
249/// struct Visible {}
250/// ```
251///
252/// ## Delegation
253///
254/// You can give an Object the ability to delegate
255/// method invocations to another object it contains.
256/// The inner object must be an `Arc`.
257/// To do so, use the `delegate_with` attribute.
258/// The attribute must contain an expression of type
259/// `FnOnce(&Self) -> Option(Arc<T>)`, where T implements Object.
260///
261/// ```
262/// use tor_rpcbase::{self as rpc, templates::*};
263/// use derive_deftly::Deftly;
264/// use std::sync::Arc;
265///
266/// #[derive(Deftly)]
267/// #[derive_deftly(Object)]
268/// struct Inner {}
269///
270/// #[derive(Deftly)]
271/// #[derive_deftly(Object)]
272/// #[deftly(rpc(
273///      delegate_with="|this: &Self| Some(this.inner.clone())",
274///      delegate_type="Inner"
275/// ))]
276/// struct Outer {
277///     inner: Arc<Inner>,
278/// }
279/// ```
280///
281    export Object expect items:
282
283
284    impl<$tgens> $ttype where
285        // We need this restriction in case there are generics
286        // that might not impl these traits.
287        $ttype: Send + Sync + 'static,
288        $twheres
289    {
290        /// Construct a new `CastTable` for this type.
291        ///
292        /// This is a function so that we can call it multiple times as
293        /// needed if the type is generic.
294        ///
295        /// Don't invoke this yourself; instead use `decl_object!`.
296        #[doc(hidden)]
297        fn make_cast_table() -> $crate::CastTable {
298            ${if tmeta(rpc(downcastable_to)) {
299                $crate::cast_table_deftness_helper!{
300                    // TODO ideally we would support multiple downcastable_to rather
301                    // than a single list, and use `as ty`
302                    ${tmeta(rpc(downcastable_to)) as token_stream}
303                }
304            } else {
305                $crate::CastTable::default()
306            }}
307        }
308    }
309
310    ${if tmeta(rpc(delegate_type)) {
311        $crate::register_delegation_note!(
312            $ttype,
313            ${tmeta(rpc(delegate_type )) as ty}
314        );
315    }}
316
317    ${if tmeta(rpc(delegate_type)) {
318        #[doc = "Delegates to [`"]
319        #[doc = ${tmeta(rpc(delegate_type)) as str}]
320        #[doc = "`]"]
321    }}
322    impl<$tgens> $crate::Object for $ttype where
323        // We need this restriction in case there are generics
324        // that might not impl these traits.
325        $ttype: Send + Sync + 'static,
326        $twheres
327    {
328        ${if tmeta(rpc(expose_outside_of_session)) {
329            fn expose_outside_of_session(&self) -> bool {
330                true
331            }
332        }}
333
334        ${if tmeta(rpc(delegate_with)) {
335            fn delegate(&self) -> Option<Arc<dyn $crate::Object>> {
336                let r: Option<Arc<${tmeta(rpc(delegate_type)) as ty}>> = (${tmeta(rpc(delegate_with)) as expr})(self);
337
338                r.map(|v| v as Arc<dyn $crate::Object>)
339            }
340        }}
341
342        fn get_cast_table(&self) -> &$crate::CastTable {
343            ${if tgens {
344                // For generic types, we have a potentially unbounded number
345                // of CastTables: one for each instantiation of the type.
346                // Therefore we keep a mutable add-only HashMap of CastTables.
347
348                use $crate::once_cell::sync::Lazy;
349                use std::sync::RwLock;
350                use std::collections::HashMap;
351                use std::any::TypeId;
352                // Map from concrete type to CastTable.
353                //
354                // Note that we use `&'static CastTable` here, not
355                // `Box<CastTable>`: If we used Box<>, the borrow checker would
356                // worry that our `CastTable`s might get freed after we returned
357                // a reference to them.  Using `&'static` guarantees that the CastTable
358                // references are safe to return.
359                //
360                // In order to get a `&'static`, we need to use Box::leak().
361                // That's fine, since we only create one CastTable per
362                // instantiation of the type.
363                static TABLES: Lazy<RwLock<HashMap<TypeId, &'static $crate::CastTable>>> =
364                Lazy::new(|| RwLock::new(HashMap::new()));
365                {
366                    let tables_r = TABLES.read().expect("poisoned lock");
367                    if let Some(table) = tables_r.get(&TypeId::of::<Self>()) {
368                        // Fast case: we already had a CastTable for this instantiation.
369                        table
370                    } else {
371                        // We didn't find a CastTable.
372                        drop(tables_r); // prevent deadlock.
373                        TABLES
374                         .write()
375                         .expect("poisoned lock")
376                         .entry(TypeId::of::<Self>())
377                         // We use `or_insert_with` here to avoid a race
378                         // condition: we only want to call make_cast_table if
379                         // one didn't already exist.
380                         .or_insert_with(|| Box::leak(Box::new(Self::make_cast_table())))
381                    }
382                }
383            } else {
384                // For non-generic types, we only ever have a single CastTable,
385                // so we can just construct it once and return it.
386                use $crate::once_cell::sync::Lazy;
387                static TABLE: Lazy<$crate::CastTable> = Lazy::new(|| $ttype::make_cast_table());
388                &TABLE
389            }}
390        }
391    }
392}
393pub use derive_deftly_template_Object;
394
395#[cfg(test)]
396mod test {
397    // @@ begin test lint list maintained by maint/add_warning @@
398    #![allow(clippy::bool_assert_comparison)]
399    #![allow(clippy::clone_on_copy)]
400    #![allow(clippy::dbg_macro)]
401    #![allow(clippy::mixed_attributes_style)]
402    #![allow(clippy::print_stderr)]
403    #![allow(clippy::print_stdout)]
404    #![allow(clippy::single_char_pattern)]
405    #![allow(clippy::unwrap_used)]
406    #![allow(clippy::unchecked_duration_subtraction)]
407    #![allow(clippy::useless_vec)]
408    #![allow(clippy::needless_pass_by_value)]
409    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
410
411    use super::*;
412    use derive_deftly::Deftly;
413
414    #[derive(Deftly)]
415    #[derive_deftly(Object)]
416    #[deftly(rpc(downcastable_to = "HasWheels"))]
417    struct Bicycle {}
418    trait HasWheels {
419        fn num_wheels(&self) -> usize;
420    }
421    impl HasWheels for Bicycle {
422        fn num_wheels(&self) -> usize {
423            2
424        }
425    }
426
427    #[derive(Deftly)]
428    #[derive_deftly(Object)]
429    struct Opossum {}
430
431    #[test]
432    fn standard_cast() {
433        let bike = Bicycle {};
434        let erased_bike: &dyn Object = &bike;
435        let has_wheels: &dyn HasWheels = erased_bike.cast_to_trait().unwrap();
436        assert_eq!(has_wheels.num_wheels(), 2);
437
438        let pogo = Opossum {};
439        let erased_pogo: &dyn Object = &pogo;
440        let has_wheels: Option<&dyn HasWheels> = erased_pogo.cast_to_trait();
441        assert!(has_wheels.is_none());
442    }
443
444    #[derive(Deftly)]
445    #[derive_deftly(Object)]
446    #[deftly(rpc(downcastable_to = "HasWheels"))]
447    struct Crowd<T: HasWheels + Send + Sync + 'static> {
448        members: Vec<T>,
449    }
450    impl<T: HasWheels + Send + Sync> HasWheels for Crowd<T> {
451        fn num_wheels(&self) -> usize {
452            self.members.iter().map(T::num_wheels).sum()
453        }
454    }
455
456    #[test]
457    fn generic_cast() {
458        let bikes = Crowd {
459            members: vec![Bicycle {}, Bicycle {}],
460        };
461        let erased_bikes: &dyn Object = &bikes;
462        let has_wheels: &dyn HasWheels = erased_bikes.cast_to_trait().unwrap();
463        assert_eq!(has_wheels.num_wheels(), 4);
464
465        let arc_bikes = Arc::new(bikes);
466        let erased_arc_bytes: Arc<dyn Object> = arc_bikes.clone();
467        let arc_has_wheels: Arc<dyn HasWheels> =
468            erased_arc_bytes.clone().cast_to_arc_trait().ok().unwrap();
469        assert_eq!(arc_has_wheels.num_wheels(), 4);
470
471        let ref_has_wheels: &dyn HasWheels = erased_arc_bytes.cast_to_trait().unwrap();
472        assert_eq!(ref_has_wheels.num_wheels(), 4);
473
474        trait SomethingElse {}
475        let arc_something_else: Result<Arc<dyn SomethingElse>, _> =
476            erased_arc_bytes.clone().cast_to_arc_trait();
477        let err_arc = arc_something_else.err().unwrap();
478        assert!(Arc::ptr_eq(&err_arc, &erased_arc_bytes));
479    }
480
481    #[derive(Deftly)]
482    #[derive_deftly(Object)]
483    #[deftly(rpc(delegate_with = "|cage: &Self| Some(cage.possum.clone())"))]
484    #[deftly(rpc(delegate_type = "Opossum"))]
485    struct PossumCage {
486        possum: Arc<Opossum>,
487    }
488}