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 extend::ext;
10
use serde::{Deserialize, Serialize};
11

            
12
use self::cast::CastTable;
13

            
14
/// An object in our RPC system to which methods can be addressed.
15
///
16
/// You shouldn't implement this trait yourself; instead, use the
17
/// [`derive_deftly(Object)`].
18
///
19
/// See the documentation for [`derive_deftly(Object)`]
20
/// for examples of how to declare and
21
/// downcast `Object`s.
22
///
23
/// [`derive_deftly(Object)`]: crate::templates::derive_deftly_template_Object
24
pub trait Object: DowncastSync + Send + Sync + 'static {
25
    /// Return true if this object should be given an identifier that allows it
26
    /// to be used outside of the session that generated it.
27
    ///
28
    /// Currently, the only use for such IDs in arti is identifying stream
29
    /// contexts in when opening a SOCKS connection: When an application opens a
30
    /// stream, it needs to declare what RPC context (like a `TorClient`) it's
31
    /// using, which requires that some identifier for that context exist
32
    /// outside of the RPC session that owns it.
33
    fn expose_outside_of_session(&self) -> bool {
34
        false
35
    }
36

            
37
    /// Return a [`CastTable`] that can be used to downcast a `dyn Object` of
38
    /// this type into various kinds of `dyn Trait` references.
39
    ///
40
    /// The default implementation of this method declares that the `Object`
41
    /// can't be downcast into any traits.
42
    ///
43
    /// You should not implement this method yourself; instead use
44
    /// [`derive_deftly(Object)`](crate::templates::derive_deftly_template_Object).
45
    fn get_cast_table(&self) -> &CastTable {
46
        &cast::EMPTY_CAST_TABLE
47
    }
48

            
49
    /// Optionally, return a delegation target for this `Object``.
50
    ///
51
    /// If method lookup fails on this object, then the `delegate`
52
6
    fn delegate(&self) -> Option<Arc<dyn Object>> {
53
6
        None
54
6
    }
55
}
56
downcast_rs::impl_downcast!(sync Object);
57

            
58
/// An identifier for an Object within the context of a Session.
59
///
60
/// These are opaque from the client's perspective.
61
#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize)]
62
#[serde(transparent)]
63
pub struct ObjectId(
64
    // (We use Box<str> to save a word here, since these don't have to be
65
    // mutable ever.)
66
    Box<str>,
67
);
68

            
69
impl AsRef<str> for ObjectId {
70
27300
    fn as_ref(&self) -> &str {
71
27300
        self.0.as_ref()
72
27300
    }
73
}
74

            
75
impl<T> From<T> for ObjectId
76
where
77
    T: Into<Box<str>>,
78
{
79
1090
    fn from(value: T) -> Self {
80
1090
        Self(value.into())
81
1090
    }
82
}
83

            
84
/// Extension trait for `Arc<dyn Object>` to support convenient
85
/// downcasting to `dyn Trait`.
86
///
87
/// You don't need to use this for downcasting to an object's concrete
88
/// type; for that, use [`downcast_rs::DowncastSync`].
89
///
90
/// # Examples
91
///
92
/// ```
93
/// use tor_rpcbase::{Object, ObjectArcExt, templates::*};
94
/// use derive_deftly::Deftly;
95
/// use std::sync::Arc;
96
///
97
/// #[derive(Deftly)]
98
/// #[derive_deftly(Object)]
99
/// #[deftly(rpc(downcastable_to = "HasFeet"))]
100
/// pub struct Frog {}
101
/// pub trait HasFeet {
102
///     fn num_feet(&self) -> usize;
103
/// }
104
/// impl HasFeet for Frog {
105
///     fn num_feet(&self) -> usize { 4 }
106
/// }
107
///
108
/// /// If `obj` is a HasFeet, return how many feet it has.
109
/// /// Otherwise, return 0.
110
/// fn check_feet(obj: Arc<dyn Object>) -> usize {
111
///     let maybe_has_feet: Option<&dyn HasFeet> = obj.cast_to_trait();
112
///     match maybe_has_feet {
113
///         Some(foot_haver) => foot_haver.num_feet(),
114
///         None => 0,
115
///     }
116
/// }
117
///
118
/// assert_eq!(check_feet(Arc::new(Frog{})), 4);
119
/// ```
120
#[ext(name = ObjectArcExt)]
121
pub impl Arc<dyn Object> {
122
    /// Try to cast this `Arc<dyn Object>` to a `T`.  On success, return a reference to
123
    /// T; on failure, return None.
124
2
    fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T> {
125
2
        let obj: &dyn Object = self.as_ref();
126
2
        obj.cast_to_trait()
127
2
    }
128

            
129
    /// Try to cast this `Arc<dyn Object>` to an `Arc<T>`.
130
4
    fn cast_to_arc_trait<T: ?Sized + 'static>(self) -> Result<Arc<T>, Arc<dyn Object>> {
131
4
        let table = self.get_cast_table();
132
4
        table.cast_object_to_arc(self.clone())
133
4
    }
134
}
135

            
136
impl dyn Object {
137
    /// Try to cast this `Object` to a `T`.  On success, return a reference to
138
    /// T; on failure, return None.
139
    ///
140
    /// This method is only for casting to `&dyn Trait`;
141
    /// see [`ObjectArcExt`] for limitations.
142
8
    pub fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T> {
143
8
        let table = self.get_cast_table();
144
8
        table.cast_object_to(self)
145
8
    }
146
}
147

            
148
define_derive_deftly! {
149
/// Allow a type to participate as an Object in the RPC system.
150
///
151
/// This template implements `Object` for the
152
/// target type, and can be used to cause objects to participate in the trait
153
/// downcasting system.
154
///
155
/// # Examples
156
///
157
/// ## Simple case, just implements `Object`.
158
///
159
/// ```
160
/// use tor_rpcbase::{self as rpc, templates::*};
161
/// use derive_deftly::Deftly;
162
///
163
/// #[derive(Default, Deftly)]
164
/// #[derive_deftly(Object)]
165
/// struct Houseplant {
166
///    oxygen_per_sec: f64,
167
///    benign_neglect: u8
168
/// }
169
///
170
/// // You can downcast an Object to a concrete type.
171
/// use downcast_rs::DowncastSync;
172
/// use std::sync::Arc;
173
/// let plant_obj: Arc<dyn rpc::Object> = Arc::new(Houseplant::default());
174
/// let as_plant: Arc<Houseplant> = plant_obj.downcast_arc().ok().unwrap();
175
/// ```
176
///
177
/// ## With trait downcasting
178
///
179
/// By default, you can use [`downcast_rs`] to downcast a `dyn Object` to its
180
/// concrete type.  If you also need to be able to downcast a `dyn Object` to a given
181
/// trait that it implements, you can use the `downcastable_to` attributes for `Object` to have
182
/// it participate in trait downcasting:
183
///
184
/// ```
185
/// use tor_rpcbase::{self as rpc, templates::*};
186
/// use derive_deftly::Deftly;
187
///
188
/// #[derive(Deftly)]
189
/// #[derive_deftly(Object)]
190
/// #[deftly(rpc(downcastable_to = "Gizmo, Doodad"))]
191
/// struct Frobnitz {}
192
///
193
/// trait Gizmo {}
194
/// trait Doodad {}
195
/// impl Gizmo for Frobnitz {}
196
/// impl Doodad for Frobnitz {}
197
///
198
/// use std::sync::Arc;
199
/// use rpc::ObjectArcExt; // for the cast_to method.
200
/// let frob_obj: Arc<dyn rpc::Object> = Arc::new(Frobnitz {});
201
/// let gizmo: &dyn Gizmo = frob_obj.cast_to_trait().unwrap();
202
/// let doodad: &dyn Doodad = frob_obj.cast_to_trait().unwrap();
203
/// ```
204
///
205
/// ## With generic objects
206
///
207
/// Right now, a generic object can't participate in our method lookup system,
208
/// but it _can_ participate in trait downcasting.  We'll try to remove this
209
/// limitation in the future.
210
///
211
/// ```
212
/// use tor_rpcbase::{self as rpc, templates::*};
213
/// use derive_deftly::Deftly;
214
///
215
/// #[derive(Deftly)]
216
/// #[derive_deftly(Object)]
217
/// #[deftly(rpc(downcastable_to = "ExampleTrait"))]
218
/// struct Generic<T,U> where T:Clone, U:PartialEq {
219
///     t: T,
220
///     u: U,
221
/// }
222
///
223
/// trait ExampleTrait {}
224
/// impl<T:Clone,U:PartialEq> ExampleTrait for Generic<T,U> {}
225
///
226
/// use std::sync::Arc;
227
/// use rpc::ObjectArcExt; // for the cast_to method.
228
/// let obj: Arc<dyn rpc::Object> = Arc::new(Generic { t: 42_u8, u: 42_u8 });
229
/// let tr: &dyn ExampleTrait = obj.cast_to_trait().unwrap();
230
/// ```
231
///
232
/// ## Making an object "exposed outside of the session"
233
///
234
/// You can flag any kind of Object so that its identifiers will be exported
235
/// outside of the local RPC session.  (Arti uses this for Objects whose
236
/// ObjectId needs to be used as a SOCKS identifier.)  To do so,
237
/// use the `expose_outside_session` attribute:
238
///
239
/// ```
240
/// use tor_rpcbase::{self as rpc, templates::*};
241
/// use derive_deftly::Deftly;
242
///
243
/// #[derive(Deftly)]
244
/// #[derive_deftly(Object)]
245
/// #[deftly(rpc(expose_outside_of_session))]
246
/// struct Visible {}
247
/// ```
248
///
249
/// ## Delegation
250
///
251
/// You can give an Object the ability to delegate
252
/// method invocations to another object it contains.
253
/// The inner object must be an `Arc`.
254
/// To do so, use the `delegate_with` attribute.
255
/// The attribute must contain an expression of type
256
/// `FnOnce(&Self) -> Option(Arc<T>)`, where T implements Object.
257
///
258
/// ```
259
/// use tor_rpcbase::{self as rpc, templates::*};
260
/// use derive_deftly::Deftly;
261
/// use std::sync::Arc;
262
///
263
/// #[derive(Deftly)]
264
/// #[derive_deftly(Object)]
265
/// struct Inner {}
266
///
267
/// #[derive(Deftly)]
268
/// #[derive_deftly(Object)]
269
/// #[deftly(rpc(
270
///      delegate_with="|this: &Self| Some(this.inner.clone())",
271
///      delegate_type="Inner"
272
/// ))]
273
/// struct Outer {
274
///     inner: Arc<Inner>,
275
/// }
276
/// ```
277
///
278
    export Object expect items:
279

            
280

            
281
    impl<$tgens> $ttype where
282
        // We need this restriction in case there are generics
283
        // that might not impl these traits.
284
        $ttype: Send + Sync + 'static,
285
        $twheres
286
    {
287
        /// Construct a new `CastTable` for this type.
288
        ///
289
        /// This is a function so that we can call it multiple times as
290
        /// needed if the type is generic.
291
        ///
292
        /// Don't invoke this yourself; instead use `decl_object!`.
293
        #[doc(hidden)]
294
10
        fn make_cast_table() -> $crate::CastTable {
295
            ${if tmeta(rpc(downcastable_to)) {
296
                $crate::cast_table_deftness_helper!{
297
                    // TODO ideally we would support multiple downcastable_to rather
298
                    // than a single list, and use `as ty`
299
                    ${tmeta(rpc(downcastable_to)) as token_stream}
300
                }
301
            } else {
302
                $crate::CastTable::default()
303
            }}
304
        }
305
    }
306

            
307
    ${if tmeta(rpc(delegate_type)) {
308
        $crate::register_delegation_note!(
309
            $ttype,
310
            ${tmeta(rpc(delegate_type )) as ty}
311
        );
312
    }}
313

            
314
    ${if tmeta(rpc(delegate_type)) {
315
        #[doc = "Delegates to [`"]
316
        #[doc = ${tmeta(rpc(delegate_type)) as str}]
317
        #[doc = "`]"]
318
    }}
319
    impl<$tgens> $crate::Object for $ttype where
320
        // We need this restriction in case there are generics
321
        // that might not impl these traits.
322
        $ttype: Send + Sync + 'static,
323
        $twheres
324
    {
325
        ${if tmeta(rpc(expose_outside_of_session)) {
326
            fn expose_outside_of_session(&self) -> bool {
327
                true
328
            }
329
        }}
330

            
331
        ${if tmeta(rpc(delegate_with)) {
332
12
            fn delegate(&self) -> Option<Arc<dyn $crate::Object>> {
333
                let r: Option<Arc<${tmeta(rpc(delegate_type)) as ty}>> = (${tmeta(rpc(delegate_with)) as expr})(self);
334

            
335
10
                r.map(|v| v as Arc<dyn $crate::Object>)
336
            }
337
        }}
338

            
339
12
        fn get_cast_table(&self) -> &$crate::CastTable {
340
            ${if tgens {
341
                // For generic types, we have a potentially unbounded number
342
                // of CastTables: one for each instantiation of the type.
343
                // Therefore we keep a mutable add-only HashMap of CastTables.
344

            
345
                use std::sync::LazyLock;
346
                use std::sync::RwLock;
347
                use std::collections::HashMap;
348
                use std::any::TypeId;
349
                // Map from concrete type to CastTable.
350
                //
351
                // Note that we use `&'static CastTable` here, not
352
                // `Box<CastTable>`: If we used Box<>, the borrow checker would
353
                // worry that our `CastTable`s might get freed after we returned
354
                // a reference to them.  Using `&'static` guarantees that the CastTable
355
                // references are safe to return.
356
                //
357
                // In order to get a `&'static`, we need to use Box::leak().
358
                // That's fine, since we only create one CastTable per
359
                // instantiation of the type.
360
                static TABLES: LazyLock<RwLock<HashMap<TypeId, &'static $crate::CastTable>>> =
361
2
                LazyLock::new(|| RwLock::new(HashMap::new()));
362
                {
363
                    let tables_r = TABLES.read().expect("poisoned lock");
364
                    if let Some(table) = tables_r.get(&TypeId::of::<Self>()) {
365
                        // Fast case: we already had a CastTable for this instantiation.
366
                        table
367
                    } else {
368
                        // We didn't find a CastTable.
369
                        drop(tables_r); // prevent deadlock.
370
                        TABLES
371
                         .write()
372
                         .expect("poisoned lock")
373
                         .entry(TypeId::of::<Self>())
374
                         // We use `or_insert_with` here to avoid a race
375
                         // condition: we only want to call make_cast_table if
376
                         // one didn't already exist.
377
2
                         .or_insert_with(|| Box::leak(Box::new(Self::make_cast_table())))
378
                    }
379
                }
380
            } else {
381
                // For non-generic types, we only ever have a single CastTable,
382
                // so we can just construct it once and return it.
383
                use std::sync::LazyLock;
384
4
                static TABLE: LazyLock<$crate::CastTable> = LazyLock::new(|| $ttype::make_cast_table());
385
                &TABLE
386
            }}
387
        }
388
    }
389
}
390
pub use derive_deftly_template_Object;
391

            
392
#[cfg(test)]
393
mod test {
394
    // @@ begin test lint list maintained by maint/add_warning @@
395
    #![allow(clippy::bool_assert_comparison)]
396
    #![allow(clippy::clone_on_copy)]
397
    #![allow(clippy::dbg_macro)]
398
    #![allow(clippy::mixed_attributes_style)]
399
    #![allow(clippy::print_stderr)]
400
    #![allow(clippy::print_stdout)]
401
    #![allow(clippy::single_char_pattern)]
402
    #![allow(clippy::unwrap_used)]
403
    #![allow(clippy::unchecked_time_subtraction)]
404
    #![allow(clippy::useless_vec)]
405
    #![allow(clippy::needless_pass_by_value)]
406
    #![allow(clippy::string_slice)] // See arti#2571
407
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
408

            
409
    use super::*;
410
    use derive_deftly::Deftly;
411

            
412
    #[derive(Deftly)]
413
    #[derive_deftly(Object)]
414
    #[deftly(rpc(downcastable_to = "HasWheels"))]
415
    struct Bicycle {}
416
    trait HasWheels {
417
        fn num_wheels(&self) -> usize;
418
    }
419
    impl HasWheels for Bicycle {
420
        fn num_wheels(&self) -> usize {
421
            2
422
        }
423
    }
424

            
425
    #[derive(Deftly, Default)]
426
    #[derive_deftly(Object)]
427
    struct Opossum {}
428

            
429
    #[test]
430
    fn standard_cast() {
431
        let bike = Bicycle {};
432
        let erased_bike: &dyn Object = &bike;
433
        let has_wheels: &dyn HasWheels = erased_bike.cast_to_trait().unwrap();
434
        assert_eq!(has_wheels.num_wheels(), 2);
435

            
436
        let pogo = Opossum {};
437
        let erased_pogo: &dyn Object = &pogo;
438
        let has_wheels: Option<&dyn HasWheels> = erased_pogo.cast_to_trait();
439
        assert!(has_wheels.is_none());
440
    }
441

            
442
    #[derive(Deftly)]
443
    #[derive_deftly(Object)]
444
    #[deftly(rpc(downcastable_to = "HasWheels"))]
445
    struct Crowd<T: HasWheels + Send + Sync + 'static> {
446
        members: Vec<T>,
447
    }
448
    impl<T: HasWheels + Send + Sync> HasWheels for Crowd<T> {
449
        fn num_wheels(&self) -> usize {
450
            self.members.iter().map(T::num_wheels).sum()
451
        }
452
    }
453

            
454
    #[test]
455
    fn generic_cast() {
456
        let bikes = Crowd {
457
            members: vec![Bicycle {}, Bicycle {}],
458
        };
459
        let erased_bikes: &dyn Object = &bikes;
460
        let has_wheels: &dyn HasWheels = erased_bikes.cast_to_trait().unwrap();
461
        assert_eq!(has_wheels.num_wheels(), 4);
462

            
463
        let arc_bikes = Arc::new(bikes);
464
        let erased_arc_bytes: Arc<dyn Object> = arc_bikes.clone();
465
        let arc_has_wheels: Arc<dyn HasWheels> =
466
            erased_arc_bytes.clone().cast_to_arc_trait().ok().unwrap();
467
        assert_eq!(arc_has_wheels.num_wheels(), 4);
468

            
469
        let ref_has_wheels: &dyn HasWheels = erased_arc_bytes.cast_to_trait().unwrap();
470
        assert_eq!(ref_has_wheels.num_wheels(), 4);
471

            
472
        trait SomethingElse {}
473
        let arc_something_else: Result<Arc<dyn SomethingElse>, _> =
474
            erased_arc_bytes.clone().cast_to_arc_trait();
475
        let err_arc = arc_something_else.err().unwrap();
476
        assert!(Arc::ptr_eq(&err_arc, &erased_arc_bytes));
477
    }
478

            
479
    #[derive(Deftly, Default)]
480
    #[derive_deftly(Object)]
481
    #[deftly(rpc(delegate_with = "|cage: &Self| Some(cage.possum.clone())"))]
482
    #[deftly(rpc(delegate_type = "Opossum"))]
483
    struct PossumCage {
484
        possum: Arc<Opossum>,
485
    }
486

            
487
    // #[allow(unused)] isn't effective for `make_cast_table` because the d-d macro doesn't
488
    // pass it through.  We don't want to add #[allow(unused)] in the macro, because (I think)
489
    // `make_cast_table` being unused is indeed telling us that we haven't registered any
490
    // method impls for this object.
491
    const _: fn() = || {
492
        // closure gives us a non-context in which to call ::default()
493
        let _: &dyn Object = &PossumCage::default();
494
    };
495
}