1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! # Code generation metadata
//!
//! The metadata is used for various stuff that would be otherwise difficult or plain impossible
//! with the current design, such as:
//! - Pass compile-time constants that would otherwise have to be hardcoded in various places (ex.
//! [PrimeModuloMeta](self::prime_modulo)).
//! - Declare FFI bindings to external libraries (ex.
//! [ReallocBindingsMeta](self::realloc_bindings)).
//! - Pass extra compilation info to the libfunc generators (ex.
//! [TailRecursionMeta](self::tail_recursion)).
use std::{
any::{Any, TypeId},
collections::{hash_map::Entry, HashMap},
};
pub mod auto_breakpoint;
pub mod debug_utils;
pub mod enum_snapshot_variants;
pub mod gas;
pub mod profiler;
pub mod realloc_bindings;
pub mod runtime_bindings;
pub mod tail_recursion;
pub mod trace_dump;
/// Metadata container.
#[cfg_attr(not(feature = "with-debug-utils"), derive(Default))]
#[derive(Debug)]
pub struct MetadataStorage {
entries: HashMap<TypeId, Box<dyn Any>>,
}
impl MetadataStorage {
/// Create an empty metadata container.
pub fn new() -> Self {
Self::default()
}
/// Insert some metadata and return a mutable reference.
///
/// The insertion will fail if there is already some metadata with the same type, in which case
/// it'll return `None`.
pub fn insert<T>(&mut self, meta: T) -> Option<&mut T>
where
T: Any,
{
if let Entry::Vacant(e) = self.entries.entry(TypeId::of::<T>()) {
e.insert(Box::new(meta));
self.get_mut::<T>()
} else {
None
}
}
/// Remove some metadata and return its last value.
///
/// The removal will fail if there is no metadata with the requested type, in which case it'll
/// return `None`.
pub fn remove<T>(&mut self) -> Option<T>
where
T: Any,
{
self.entries.remove(&TypeId::of::<T>()).map(|meta| {
*(Box::<dyn Any + 'static>::downcast::<T>(meta).expect(
"attempt to downcast a boxed value to a type which does not match the actual",
))
})
}
/// Retrieve a reference to some metadata.
///
/// The retrieval will fail if there is no metadata with the requested type, in which case it'll
/// return `None`.
pub fn get<T>(&self) -> Option<&T>
where
T: Any,
{
self.entries.get(&TypeId::of::<T>()).map(|meta| {
meta.downcast_ref::<T>().expect(
"attempt to downcast a boxed value to a type which does not match the actual",
)
})
}
/// Retrieve a mutable reference to some metadata.
///
/// The retrieval will fail if there is no metadata with the requested type, in which case it'll
/// return `None`.
pub fn get_mut<T>(&mut self) -> Option<&mut T>
where
T: Any,
{
self.entries.get_mut(&TypeId::of::<T>()).map(|meta| {
meta.downcast_mut::<T>()
.expect("the given type does not match the actual")
})
}
pub fn get_or_insert_with<T>(&mut self, meta_gen: impl FnOnce() -> T) -> &mut T
where
T: Any,
{
self.entries
.entry(TypeId::of::<T>())
.or_insert_with(|| Box::new(meta_gen()))
.downcast_mut::<T>()
.expect("the given type does not match the actual")
}
}
#[cfg(feature = "with-debug-utils")]
impl Default for MetadataStorage {
fn default() -> Self {
let mut metadata = Self {
entries: Default::default(),
};
metadata.insert(debug_utils::DebugUtils::default());
metadata
}
}
#[cfg(test)]
mod test {
use super::{runtime_bindings::RuntimeBindingsMeta, *};
#[test]
fn runtime_library_insert_works() {
// Create a new instance of MetadataStorage.
let mut metadata = MetadataStorage::new();
// Insert a new instance of RuntimeBindingsMeta.
let ret = metadata.insert(RuntimeBindingsMeta::default());
// Check that the insertion was successful by asserting that `ret` is not `None`.
assert!(ret.is_some());
}
#[test]
fn runtime_library_insert_non_vacant() {
// Create a new instance of MetadataStorage.
let mut metadata = MetadataStorage::new();
// Insert a new instance of RuntimeBindingsMeta.
let ret_vacant = metadata.insert(RuntimeBindingsMeta::default());
// Check that the first insertion was successful.
assert!(ret_vacant.is_some());
// Attempt to insert another instance of RuntimeBindingsMeta.
let ret_non_vacant = metadata.insert(RuntimeBindingsMeta::default());
// Check that the second insertion failed because an instance of the same type already exists.
assert!(ret_non_vacant.is_none());
}
}