/ crates / tor-rpcbase / src / obj.rs
obj.rs
  1  //! Object type for our RPC system.
  2  
  3  pub(crate) mod cast;
  4  
  5  use std::sync::Arc;
  6  
  7  use derive_deftly::define_derive_deftly;
  8  use downcast_rs::DowncastSync;
  9  use serde::{Deserialize, Serialize};
 10  
 11  use 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
 23  pub 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  }
 55  downcast_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)]
 62  pub 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  
 68  impl AsRef<str> for ObjectId {
 69      fn as_ref(&self) -> &str {
 70          self.0.as_ref()
 71      }
 72  }
 73  
 74  impl<T> From<T> for ObjectId
 75  where
 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  /// ```
119  pub 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  
128  impl 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  
140  impl 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  
151  define_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  }
393  pub use derive_deftly_template_Object;
394  
395  #[cfg(test)]
396  mod 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  }