tempo_precompiles_macros/
layout.rs1use crate::{
2 FieldKind,
3 packing::{self, LayoutField, PackingConstants, SlotAssignment},
4};
5use quote::{format_ident, quote};
6use syn::{Expr, Ident, Visibility};
7
8pub(crate) fn gen_handler_field_decl(field: &LayoutField<'_>) -> proc_macro2::TokenStream {
10 let field_name = field.name;
11 let handler_type = match &field.kind {
12 FieldKind::Direct(ty) => {
13 quote! { <#ty as crate::storage::StorableType>::Handler }
14 }
15 FieldKind::Mapping { key, value } => {
16 quote! { <crate::storage::Mapping<#key, #value> as crate::storage::StorableType>::Handler }
17 }
18 };
19
20 quote! {
21 pub #field_name: #handler_type
22 }
23}
24
25pub(crate) fn gen_handler_field_init(
35 field: &LayoutField<'_>,
36 field_idx: usize,
37 all_fields: &[LayoutField<'_>],
38 packing_mod: Option<&Ident>,
39) -> proc_macro2::TokenStream {
40 let field_name = field.name;
41 let consts = PackingConstants::new(field_name);
42 let (loc_const, (slot_const, offset_const)) = (consts.location(), consts.into_tuple());
43
44 let is_contract = packing_mod.is_none();
45
46 let slots_mod = format_ident!("slots");
48 let const_mod = packing_mod.unwrap_or(&slots_mod);
49
50 let slot_expr = if is_contract {
52 quote! { #const_mod::#slot_const }
53 } else {
54 quote! { base_slot.saturating_add(::alloy::primitives::U256::from_limbs([#const_mod::#loc_const.offset_slots as u64, 0, 0, 0])) }
55 };
56
57 match &field.kind {
58 FieldKind::Direct(ty) => {
59 let (prev_slot_const_ref, next_slot_const_ref) = packing::get_neighbor_slot_refs(
61 field_idx,
62 all_fields,
63 const_mod,
64 |f| f.name,
65 is_contract,
66 );
67
68 let layout_ctx = if is_contract {
70 packing::gen_layout_ctx_expr(
71 ty,
72 matches!(field.assigned_slot, SlotAssignment::Manual(_)),
73 quote! { #const_mod::#slot_const },
74 quote! { #const_mod::#offset_const },
75 prev_slot_const_ref,
76 next_slot_const_ref,
77 )
78 } else {
79 packing::gen_layout_ctx_expr(
80 ty,
81 false, quote! { #const_mod::#loc_const.offset_slots },
83 quote! { #const_mod::#loc_const.offset_bytes },
84 prev_slot_const_ref,
85 next_slot_const_ref,
86 )
87 };
88
89 quote! {
90 #field_name: <#ty as crate::storage::StorableType>::handle(
91 #slot_expr, #layout_ctx, address
92 )
93 }
94 }
95 FieldKind::Mapping { key, value } => {
96 quote! {
97 #field_name: <crate::storage::Mapping<#key, #value> as crate::storage::StorableType>::handle(
98 #slot_expr, crate::storage::LayoutCtx::FULL, address
99 )
100 }
101 }
102 }
103}
104
105pub(crate) fn gen_struct(
107 name: &Ident,
108 vis: &Visibility,
109 allocated_fields: &[LayoutField<'_>],
110) -> proc_macro2::TokenStream {
111 let handler_fields = allocated_fields.iter().map(gen_handler_field_decl);
113
114 quote! {
115 #vis struct #name {
116 #(#handler_fields,)*
117 address: ::alloy::primitives::Address,
118 storage: crate::storage::StorageCtx,
119 }
120 }
121}
122
123pub(crate) fn gen_constructor(
125 name: &Ident,
126 allocated_fields: &[LayoutField<'_>],
127 address: Option<&Expr>,
128) -> proc_macro2::TokenStream {
129 let field_inits = allocated_fields
131 .iter()
132 .enumerate()
133 .map(|(idx, field)| gen_handler_field_init(field, idx, allocated_fields, None));
134
135 let new_fn = address.map(|addr| {
137 quote! {
138 pub fn new() -> Self {
142 Self::__new(#addr)
143 }
144 }
145 });
146
147 quote! {
148 impl #name {
149 #new_fn
150
151 #[inline(always)]
152 fn __new(address: ::alloy::primitives::Address) -> Self {
153 #[cfg(debug_assertions)]
155 {
156 slots::__check_all_collisions();
157 }
158
159 Self {
160 #(#field_inits,)*
161 address,
162 storage: crate::storage::StorageCtx::default(),
163 }
164 }
165
166 #[inline(always)]
167 fn __initialize(&mut self) -> crate::error::Result<()> {
168 let bytecode = ::revm::state::Bytecode::new_legacy(::alloy::primitives::Bytes::from_static(&[0xef]));
169 self.storage.set_code(self.address, bytecode)?;
170
171 Ok(())
172 }
173
174 #[inline(always)]
175 fn emit_event(&mut self, event: impl ::alloy::primitives::IntoLogData) -> crate::error::Result<()> {
176 self.storage.emit_event(self.address, event.into_log_data())
177 }
178
179 #[cfg(any(test, feature = "test-utils"))]
180 pub fn emitted_events(&self) -> &Vec<::alloy::primitives::LogData> {
181 self.storage.get_events(self.address)
182 }
183
184 #[cfg(any(test, feature = "test-utils"))]
185 pub fn clear_emitted_events(&mut self) {
186 self.storage.clear_events(self.address);
187 }
188
189 #[cfg(any(test, feature = "test-utils"))]
190 pub fn assert_emitted_events(&self, expected: Vec<impl ::alloy::primitives::IntoLogData>) {
191 let emitted = self.storage.get_events(self.address);
192 assert_eq!(emitted.len(), expected.len());
193
194 for (i, event) in expected.into_iter().enumerate() {
195 assert_eq!(emitted[i], event.into_log_data());
196 }
197 }
198 }
199 }
200}
201
202pub(crate) fn gen_contract_storage_impl(name: &Ident) -> proc_macro2::TokenStream {
204 quote! {
205 impl crate::storage::ContractStorage for #name {
206 #[inline(always)]
207 fn address(&self) -> ::alloy::primitives::Address {
208 self.address
209 }
210
211 #[inline(always)]
212 fn storage(&self) -> &crate::storage::StorageCtx {
213 &self.storage
214 }
215
216 #[inline(always)]
217 fn storage_mut(&mut self) -> &mut crate::storage::StorageCtx {
218 &mut self.storage
219 }
220 }
221 }
222}
223
224pub(crate) fn gen_slots_module(allocated_fields: &[LayoutField<'_>]) -> proc_macro2::TokenStream {
228 let constants = packing::gen_constants_from_ir(allocated_fields, false);
230 let collision_checks = gen_collision_checks(allocated_fields);
231
232 quote! {
233 pub mod slots {
234 use super::*;
235
236 #constants
237 #collision_checks
238 }
239 }
240}
241
242fn gen_collision_checks(allocated_fields: &[LayoutField<'_>]) -> proc_macro2::TokenStream {
244 let mut generated = proc_macro2::TokenStream::new();
245 let mut check_fn_calls = Vec::new();
246
247 for (idx, allocated) in allocated_fields.iter().enumerate() {
249 let (check_fn_name, check_fn) =
250 packing::gen_collision_check_fn(idx, allocated, allocated_fields);
251 generated.extend(check_fn);
252 check_fn_calls.push(check_fn_name);
253 }
254
255 generated.extend(quote! {
258 #[cfg(debug_assertions)]
259 #[inline(always)]
260 pub(super) fn __check_all_collisions() {
261 #(#check_fn_calls();)*
262 }
263 });
264
265 generated
266}
267
268pub(crate) fn gen_default_impl(name: &Ident) -> proc_macro2::TokenStream {
272 quote! {
273 impl ::core::default::Default for #name {
274 fn default() -> Self {
275 Self::new()
276 }
277 }
278 }
279}