1use std::any::Any;
2use std::{fmt, iter};
3
4use rustc_abi::Size;
5
6use crate::*;
7
8pub const OPAQUE_OBJ_COOKIE: u8 = 0x42;
13
14pub trait MetaObj: Any {
17 fn on_access<'tcx>(&self) -> InterpResult<'tcx> {
19 interp_ok(())
20 }
21
22 fn opaque(&self) -> bool;
25}
26
27impl dyn MetaObj {
28 #[inline(always)]
29 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
30 let x: &dyn Any = self;
31 x.downcast_ref()
32 }
33
34 #[inline(always)]
35 pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
36 let x: &mut dyn Any = self;
37 x.downcast_mut()
38 }
39
40 #[inline(always)]
41 pub fn downcast<T: Any>(self: Box<Self>) -> Option<Box<T>> {
42 let x: Box<dyn Any> = self;
43 x.downcast().ok()
44 }
45}
46
47impl fmt::Debug for dyn MetaObj {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 f.debug_struct("MetaObj").finish_non_exhaustive()
50 }
51}
52
53impl<'tcx> AllocExtra<'tcx> {
54 fn get_meta<T: MetaObj>(&self, offset: Size) -> Option<&T> {
56 self.meta_objs.get_start(offset).and_then(|s| s.downcast_ref::<T>())
57 }
58
59 fn get_meta_mut<T: MetaObj>(&mut self, offset: Size) -> Option<&mut T> {
61 self.meta_objs.get_start_mut(offset).and_then(|s| s.downcast_mut::<T>())
62 }
63
64 fn insert_meta(&mut self, range: AllocRange, obj: Box<dyn MetaObj>) {
67 use data_structures::range_object_map::AccessType;
68 let pos = match self.meta_objs.access_type(range) {
69 AccessType::PerfectlyOverlapping(pos) => {
70 self.meta_objs.remove_from_pos(pos);
71 pos
72 }
73 AccessType::Empty(pos) => pos,
74 AccessType::ImperfectlyOverlapping(pos_range) => {
75 self.meta_objs.remove_pos_range(pos_range.clone());
76 pos_range.start
77 }
78 };
79 self.meta_objs.insert_at_pos(pos, range, obj);
80 }
81
82 fn remove_meta(&mut self, range: AllocRange) -> Option<Box<dyn MetaObj>> {
83 use data_structures::range_object_map::AccessType;
84 match self.meta_objs.access_type(range) {
85 AccessType::PerfectlyOverlapping(pos) => Some(self.meta_objs.remove_from_pos(pos)),
86 _ => None,
87 }
88 }
89}
90
91#[derive(Debug, Copy, Clone)]
92pub enum GetMetaError {
93 NoMem,
95 NoObj,
97}
98
99impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
100pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
101 fn try_get_meta<'a, T: MetaObj>(&'a self, ptr: Pointer) -> Result<&'a T, GetMetaError>
103 where
104 'tcx: 'a,
105 {
106 let ecx = self.eval_context_ref();
107 let Ok((alloc_id, offset, ..)) = ecx.ptr_try_get_alloc_id(ptr, 0) else {
108 return Err(GetMetaError::NoMem);
109 };
110 let info = ecx.get_alloc_info(alloc_id);
111 if info.kind != AllocKind::LiveData || offset >= info.size {
112 return Err(GetMetaError::NoMem);
113 }
114 let alloc_extra = ecx.get_alloc_extra(alloc_id).unwrap();
116 alloc_extra.get_meta::<T>(offset).ok_or(GetMetaError::NoObj)
117 }
118
119 fn insert_meta(&mut self, place: &MPlaceTy<'tcx>, obj: Box<dyn MetaObj>) -> InterpResult<'tcx> {
120 let ecx = self.eval_context_mut();
121 assert!(place.layout.size > Size::ZERO);
122
123 if obj.opaque() {
127 ecx.write_bytes_ptr(
128 place.ptr(),
129 iter::repeat_n(OPAQUE_OBJ_COOKIE, place.layout.size.bytes_usize()),
130 )?;
131 } else {
132 ecx.check_ptr_access(place.ptr(), place.layout.size, CheckInAllocMsg::MemoryAccess)?;
133 }
134
135 let (alloc_id, offset, ..) = ecx.ptr_try_get_alloc_id(place.ptr(), 0).unwrap();
137 let (alloc_extra, _machine) = ecx.get_alloc_extra_mut(alloc_id)?;
138 alloc_extra.insert_meta(AllocRange { start: offset, size: place.layout.size }, obj);
139 interp_ok(())
140 }
141
142 fn get_or_insert_meta<'a, T: MetaObj>(
143 &'a mut self,
144 place: &MPlaceTy<'tcx>,
145 obj: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
146 ) -> InterpResult<'tcx, &'a mut T>
147 where
148 'tcx: 'a,
149 {
150 let ecx = self.eval_context_mut();
151 let (alloc_id, offset, ..) = ecx.ptr_get_alloc_id(place.ptr(), 0)?;
152 let alloc_extra = ecx.get_alloc_extra(alloc_id)?;
154 if alloc_extra.get_meta::<T>(offset).is_none() {
155 let obj = obj(ecx)?;
157 ecx.insert_meta(place, Box::new(obj))?;
158 }
159 let (alloc_extra, _machine) = ecx.get_alloc_extra_mut(alloc_id).unwrap();
161 interp_ok(alloc_extra.get_meta_mut(offset).unwrap())
162 }
163
164 fn remove_meta<'a, T: MetaObj>(
165 &'a mut self,
166 place: &MPlaceTy<'tcx>,
167 ) -> InterpResult<'tcx, Option<Box<T>>> {
168 let ecx = self.eval_context_mut();
169 let (alloc_id, offset, ..) = ecx.ptr_get_alloc_id(place.ptr(), 0)?;
170 let (alloc_extra, _machine) = ecx.get_alloc_extra_mut(alloc_id).unwrap();
171
172 interp_ok(
173 alloc_extra
174 .remove_meta(AllocRange { start: offset, size: place.layout.size })
175 .and_then(|obj| obj.downcast()),
176 )
177 }
178}