Skip to main content

capnp/
introspect.rs

1//! Traits and types to support run-time type introspection, i.e. reflection.
2
3use crate::private::layout::ElementSize;
4use crate::schema::{EnumSchema, StructSchema};
5
6/// A type that supports reflection. All types that can appear in a Cap'n Proto message
7/// implement this trait.
8pub trait Introspect {
9    /// Retrieves a description of the type.
10    fn introspect() -> Type;
11}
12
13/// A description of a Cap'n Proto type. The representation is
14/// optimized to avoid heap allocation.
15///
16/// To examine a `Type`, you should call the `which()` method.
17#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
18pub struct Type {
19    /// The type, minus any outer `List( )`.
20    base: BaseType,
21
22    /// How many times `base` is wrapped in `List( )`.
23    list_count: usize,
24}
25
26impl Type {
27    /// Constructs a new `Type` that is not a list.
28    fn new_base(base: BaseType) -> Self {
29        Self {
30            base,
31            list_count: 0,
32        }
33    }
34
35    /// Constructs a new `Type` that is a list wrapping some other `Type`.
36    pub fn list_of(mut element_type: Type) -> Self {
37        element_type.list_count += 1;
38        element_type
39    }
40
41    /// Unfolds a single layer of the `Type`, to allow for pattern matching.
42    pub fn which(&self) -> TypeVariant {
43        if self.list_count > 0 {
44            TypeVariant::List(Type {
45                base: self.base,
46                list_count: self.list_count - 1,
47            })
48        } else {
49            match self.base {
50                BaseType::Void => TypeVariant::Void,
51                BaseType::Bool => TypeVariant::Bool,
52                BaseType::Int8 => TypeVariant::Int8,
53                BaseType::Int16 => TypeVariant::Int16,
54                BaseType::Int32 => TypeVariant::Int32,
55                BaseType::Int64 => TypeVariant::Int64,
56                BaseType::UInt8 => TypeVariant::UInt8,
57                BaseType::UInt16 => TypeVariant::UInt16,
58                BaseType::UInt32 => TypeVariant::UInt32,
59                BaseType::UInt64 => TypeVariant::UInt64,
60                BaseType::Float32 => TypeVariant::Float32,
61                BaseType::Float64 => TypeVariant::Float64,
62                BaseType::Text => TypeVariant::Text,
63                BaseType::Data => TypeVariant::Data,
64                BaseType::Enum(re) => TypeVariant::Enum(re),
65                BaseType::Struct(rs) => TypeVariant::Struct(rs),
66                BaseType::AnyPointer => TypeVariant::AnyPointer,
67                BaseType::Capability => TypeVariant::Capability,
68            }
69        }
70    }
71
72    /// If this type T appears as List(T), then what is the expected
73    /// element size of the list?
74    pub(crate) fn expected_element_size(&self) -> ElementSize {
75        if self.list_count > 0 {
76            ElementSize::Pointer
77        } else {
78            match self.base {
79                BaseType::Void => ElementSize::Void,
80                BaseType::Bool => ElementSize::Bit,
81                BaseType::Int8 | BaseType::UInt8 => ElementSize::Byte,
82                BaseType::Int16 | BaseType::UInt16 | BaseType::Enum(_) => ElementSize::TwoBytes,
83                BaseType::Int32 | BaseType::UInt32 | BaseType::Float32 => ElementSize::FourBytes,
84                BaseType::Int64 | BaseType::UInt64 | BaseType::Float64 => ElementSize::EightBytes,
85                BaseType::Text | BaseType::Data | BaseType::AnyPointer | BaseType::Capability => {
86                    ElementSize::Pointer
87                }
88                BaseType::Struct(_) => ElementSize::InlineComposite,
89            }
90        }
91    }
92
93    /// Is the `Type` a pointer type?
94    pub fn is_pointer_type(&self) -> bool {
95        if self.list_count > 0 {
96            true
97        } else {
98            matches!(
99                self.base,
100                BaseType::Text
101                    | BaseType::Data
102                    | BaseType::AnyPointer
103                    | BaseType::Struct(_)
104                    | BaseType::Capability
105            )
106        }
107    }
108
109    /// Returns true if `self` is equal to `other` modulo
110    /// type parameters and interface types.
111    #[deprecated(
112        since = "0.27.0",
113        note = "Type now implements Eq. loose_equals ignores generics on structs, while Eq is more precise and most likely the one you want."
114    )]
115    pub fn loose_equals(&self, other: Self) -> bool {
116        match (self.which(), other.which()) {
117            (TypeVariant::Void, TypeVariant::Void) => true,
118            (TypeVariant::Bool, TypeVariant::Bool) => true,
119            (TypeVariant::UInt8, TypeVariant::UInt8) => true,
120            (TypeVariant::UInt16, TypeVariant::UInt16) => true,
121            (TypeVariant::UInt32, TypeVariant::UInt32) => true,
122            (TypeVariant::UInt64, TypeVariant::UInt64) => true,
123            (TypeVariant::Int8, TypeVariant::Int8) => true,
124            (TypeVariant::Int16, TypeVariant::Int16) => true,
125            (TypeVariant::Int32, TypeVariant::Int32) => true,
126            (TypeVariant::Int64, TypeVariant::Int64) => true,
127            (TypeVariant::Float32, TypeVariant::Float32) => true,
128            (TypeVariant::Float64, TypeVariant::Float64) => true,
129            (TypeVariant::Text, TypeVariant::Text) => true,
130            (TypeVariant::Data, TypeVariant::Data) => true,
131            (TypeVariant::Enum(es1), TypeVariant::Enum(es2)) => es1 == es2,
132            (TypeVariant::Struct(rbs1), TypeVariant::Struct(rbs2)) => {
133                // Ignore any type parameters. The original intent was that
134                // we would additionally check that the `field_types` fields
135                // were equal function pointers here. However, according to
136                // Miri's behavior at least, that check returns `false`
137                // more than we would like it to. So we settle for being
138                // a bit more accepting.
139                core::ptr::eq(rbs1.generic, rbs2.generic)
140            }
141            (TypeVariant::List(element1), TypeVariant::List(element2)) => {
142                element1.loose_equals(element2)
143            }
144            (TypeVariant::AnyPointer, TypeVariant::AnyPointer) => true,
145            (TypeVariant::Capability, TypeVariant::Capability) => true,
146            _ => false,
147        }
148    }
149}
150
151#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
152/// A `Type` unfolded one level. Suitable for pattern matching. Can be trivially
153/// converted to `Type` via the `From`/`Into` traits.
154pub enum TypeVariant {
155    Void,
156    Bool,
157    Int8,
158    Int16,
159    Int32,
160    Int64,
161    UInt8,
162    UInt16,
163    UInt32,
164    UInt64,
165    Float32,
166    Float64,
167    Text,
168    Data,
169    Struct(RawBrandedStructSchema),
170    AnyPointer,
171    Capability,
172    Enum(RawEnumSchema),
173    List(Type),
174}
175
176impl From<TypeVariant> for Type {
177    fn from(tv: TypeVariant) -> Type {
178        match tv {
179            TypeVariant::Void => Type::new_base(BaseType::Void),
180            TypeVariant::Bool => Type::new_base(BaseType::Bool),
181            TypeVariant::Int8 => Type::new_base(BaseType::Int8),
182            TypeVariant::Int16 => Type::new_base(BaseType::Int16),
183            TypeVariant::Int32 => Type::new_base(BaseType::Int32),
184            TypeVariant::Int64 => Type::new_base(BaseType::Int64),
185            TypeVariant::UInt8 => Type::new_base(BaseType::UInt8),
186            TypeVariant::UInt16 => Type::new_base(BaseType::UInt16),
187            TypeVariant::UInt32 => Type::new_base(BaseType::UInt32),
188            TypeVariant::UInt64 => Type::new_base(BaseType::UInt64),
189            TypeVariant::Float32 => Type::new_base(BaseType::Float32),
190            TypeVariant::Float64 => Type::new_base(BaseType::Float64),
191            TypeVariant::Text => Type::new_base(BaseType::Text),
192            TypeVariant::Data => Type::new_base(BaseType::Data),
193            TypeVariant::Struct(rbs) => Type::new_base(BaseType::Struct(rbs)),
194            TypeVariant::AnyPointer => Type::new_base(BaseType::AnyPointer),
195            TypeVariant::Capability => Type::new_base(BaseType::Capability),
196            TypeVariant::Enum(es) => Type::new_base(BaseType::Enum(es)),
197            TypeVariant::List(list) => Type::list_of(list),
198        }
199    }
200}
201
202/// A Cap'n Proto type, excluding `List`.
203#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
204enum BaseType {
205    Void,
206    Bool,
207    Int8,
208    Int16,
209    Int32,
210    Int64,
211    UInt8,
212    UInt16,
213    UInt32,
214    UInt64,
215    Float32,
216    Float64,
217    Text,
218    Data,
219    Struct(RawBrandedStructSchema),
220    AnyPointer,
221    Capability,
222    Enum(RawEnumSchema),
223}
224
225macro_rules! primitive_introspect(
226    ($t:ty, $v:ident) => (
227        impl Introspect for $t {
228            fn introspect() -> Type { Type::new_base(BaseType::$v) }
229        }
230    )
231);
232
233primitive_introspect!((), Void);
234primitive_introspect!(bool, Bool);
235primitive_introspect!(i8, Int8);
236primitive_introspect!(i16, Int16);
237primitive_introspect!(i32, Int32);
238primitive_introspect!(i64, Int64);
239primitive_introspect!(u8, UInt8);
240primitive_introspect!(u16, UInt16);
241primitive_introspect!(u32, UInt32);
242primitive_introspect!(u64, UInt64);
243primitive_introspect!(f32, Float32);
244primitive_introspect!(f64, Float64);
245
246/// Type information that gets included in the generated code for every
247/// user-defined Cap'n Proto struct.
248#[derive(Copy, Clone)]
249pub struct RawStructSchema {
250    /// The Node (as defined in schema.capnp), as a single segment message.
251    pub(crate) arena: &'static crate::private::arena::GeneratedCodeArena,
252
253    /// Indices (not ordinals) of fields that don't have a discriminant value.
254    pub(crate) nonunion_members: &'static [u16],
255
256    /// Map from discriminant value to field index.
257    pub(crate) members_by_discriminant: &'static [u16],
258
259    /// Indices of fields, sorted by their respective names.
260    pub(crate) members_by_name: &'static [u16],
261}
262
263impl RawStructSchema {
264    /// Constructs a new `RawStructSchema`.
265    pub const fn new(
266        arena: &'static crate::private::arena::GeneratedCodeArena,
267        nonunion_members: &'static [u16],
268        members_by_discriminant: &'static [u16],
269        members_by_name: &'static [u16],
270    ) -> Self {
271        Self {
272            arena,
273            nonunion_members,
274            members_by_discriminant,
275            members_by_name,
276        }
277    }
278}
279
280/// A RawStructSchema with branding information, i.e. resolution of type parameters.
281/// To use one of this, you will usually want to convert it to a `schema::StructSchema`,
282/// which can be done via `into()`.
283#[derive(Copy, Clone)]
284pub struct RawBrandedStructSchema {
285    /// The unbranded base schema.
286    pub generic: &'static RawStructSchema,
287
288    /// Map from field index (not ordinal) to Type.
289    pub field_types: fn(u16) -> Type,
290
291    /// Map from (maybe field index, annotation index) to the Type
292    /// of the value held by that annotation.
293    pub annotation_types: fn(Option<u16>, u32) -> Type,
294
295    /// Used to compare schemas at runtime - the TypeId of the Owned struct that
296    /// this schema describes, including its branding.
297    pub type_id: ::core::any::TypeId,
298}
299
300impl ::core::cmp::PartialEq for RawBrandedStructSchema {
301    fn eq(&self, other: &Self) -> bool {
302        self.type_id == other.type_id
303    }
304}
305impl ::core::cmp::Eq for RawBrandedStructSchema {}
306impl ::core::hash::Hash for RawBrandedStructSchema {
307    fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
308        self.type_id.hash(state);
309    }
310}
311
312impl core::fmt::Debug for RawBrandedStructSchema {
313    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> {
314        write!(
315            f,
316            "RawBrandedStructSchema({:?}, {:?})",
317            self.generic as *const _, self.field_types as *const fn(u16) -> Type
318        )
319    }
320}
321
322impl From<StructSchema> for RawBrandedStructSchema {
323    fn from(value: StructSchema) -> Self {
324        value.raw
325    }
326}
327
328/// Type information that gets included in the generated code for every
329/// user-defined Cap'n Proto enum.
330///
331/// To use one of these, you will usually want to convert it to a `schema::EnumSchema`,
332/// which can be done via `into()`.
333#[derive(Clone, Copy)]
334pub struct RawEnumSchema {
335    /// The Node (as defined in schema.capnp), as a single segment message.
336    pub(crate) arena: &'static crate::private::arena::GeneratedCodeArena,
337
338    /// Map from (maybe enumerant index, annotation index) to the Type
339    /// of the value held by that annotation.
340    pub(crate) annotation_types: fn(Option<u16>, u32) -> Type,
341}
342
343impl core::cmp::PartialEq for RawEnumSchema {
344    fn eq(&self, other: &Self) -> bool {
345        ::core::ptr::eq(self.arena, other.arena)
346    }
347}
348
349impl core::cmp::Eq for RawEnumSchema {}
350impl core::hash::Hash for RawEnumSchema {
351    fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
352        (self.arena as *const crate::private::arena::GeneratedCodeArena).hash(state);
353    }
354}
355
356impl core::fmt::Debug for RawEnumSchema {
357    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> {
358        write!(f, "RawEnumSchema({:?})", self.arena as *const _)
359    }
360}
361
362impl RawEnumSchema {
363    /// Constructs a new `RawEnumSchema`.
364    pub const fn new(
365        arena: &'static crate::private::arena::GeneratedCodeArena,
366        annotation_types: fn(Option<u16>, u32) -> Type,
367    ) -> Self {
368        Self {
369            arena,
370            annotation_types,
371        }
372    }
373}
374
375impl From<EnumSchema> for RawEnumSchema {
376    fn from(value: EnumSchema) -> Self {
377        value.raw
378    }
379}
380
381/**
382Function intended to be called by generated `get_field_types()` methods.
383Defined here so that we can use inline format args syntax, which did
384not exist before Rust edition 2021. Not intended to be called directly by
385end users.
386 */
387pub fn panic_invalid_field_index(index: u16) -> ! {
388    panic!("invalid field index {index}")
389}
390
391/**
392Function intended to be called by generated `get_annotation_types()` methods.
393Defined here so that we can use inline format args syntax, which did
394not exist before Rust edition 2021. Not intended to be called directly by
395end users.
396 */
397pub fn panic_invalid_annotation_indices(child_index: Option<u16>, index: u32) -> ! {
398    panic!("invalid annotation indices ({child_index:?}, {index})")
399}