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) =
61 packing::get_neighbor_slot_refs(field_idx, all_fields, const_mod, |f| f.name);
62
63 let layout_ctx = if is_contract {
65 packing::gen_layout_ctx_expr_inefficient(
70 ty,
71 matches!(field.assigned_slot, SlotAssignment::Manual(_)),
72 quote! { #const_mod::#slot_const },
73 quote! { #const_mod::#offset_const },
74 prev_slot_const_ref,
75 next_slot_const_ref,
76 )
77 } else {
78 packing::gen_layout_ctx_expr(
79 ty,
80 false, quote! { #const_mod::#loc_const.offset_slots },
82 quote! { #const_mod::#loc_const.offset_bytes },
83 prev_slot_const_ref,
84 next_slot_const_ref,
85 )
86 };
87
88 quote! {
89 #field_name: <#ty as crate::storage::StorableType>::handle(
90 #slot_expr, #layout_ctx, address
91 )
92 }
93 }
94 FieldKind::Mapping { key, value } => {
95 quote! {
96 #field_name: <crate::storage::Mapping<#key, #value> as crate::storage::StorableType>::handle(
97 #slot_expr, crate::storage::LayoutCtx::FULL, address
98 )
99 }
100 }
101 }
102}
103
104pub(crate) fn gen_struct(
106 name: &Ident,
107 vis: &Visibility,
108 allocated_fields: &[LayoutField<'_>],
109) -> proc_macro2::TokenStream {
110 let handler_fields = allocated_fields.iter().map(gen_handler_field_decl);
112
113 quote! {
114 #vis struct #name {
115 #(#handler_fields,)*
116 address: ::alloy::primitives::Address,
117 storage: crate::storage::StorageCtx,
118 }
119 }
120}
121
122pub(crate) fn gen_constructor(
124 name: &Ident,
125 allocated_fields: &[LayoutField<'_>],
126 address: Option<&Expr>,
127) -> proc_macro2::TokenStream {
128 let field_inits = allocated_fields
130 .iter()
131 .enumerate()
132 .map(|(idx, field)| gen_handler_field_init(field, idx, allocated_fields, None));
133
134 let new_fn = address.map(|addr| {
136 quote! {
137 pub fn new() -> Self {
141 Self::__new(#addr)
142 }
143 }
144 });
145
146 quote! {
147 impl #name {
148 #new_fn
149
150 #[inline(always)]
151 fn __new(address: ::alloy::primitives::Address) -> Self {
152 #[cfg(debug_assertions)]
154 {
155 slots::__check_all_collisions();
156 }
157
158 Self {
159 #(#field_inits,)*
160 address,
161 storage: crate::storage::StorageCtx::default(),
162 }
163 }
164
165 #[inline(always)]
166 fn __initialize(&mut self) -> crate::error::Result<()> {
167 let bytecode = ::revm::state::Bytecode::new_legacy(::alloy::primitives::Bytes::from_static(&[0xef]));
168 self.storage.set_code(self.address, bytecode)?;
169
170 Ok(())
171 }
172
173 #[inline(always)]
174 fn emit_event(&mut self, event: impl ::alloy::primitives::IntoLogData) -> crate::error::Result<()> {
175 self.storage.emit_event(self.address, event.into_log_data())
176 }
177
178 #[cfg(any(test, feature = "test-utils"))]
179 fn emitted_events(&self) -> &Vec<::alloy::primitives::LogData> {
180 self.storage.get_events(self.address)
181 }
182
183 #[cfg(any(test, feature = "test-utils"))]
184 fn assert_emitted_events(&self, expected: Vec<impl ::alloy::primitives::IntoLogData>) {
185 let emitted = self.storage.get_events(self.address);
186 assert_eq!(emitted.len(), expected.len());
187
188 for (i, event) in expected.into_iter().enumerate() {
189 assert_eq!(emitted[i], event.into_log_data());
190 }
191 }
192 }
193 }
194}
195
196pub(crate) fn gen_contract_storage_impl(name: &Ident) -> proc_macro2::TokenStream {
198 quote! {
199 impl crate::storage::ContractStorage for #name {
200 #[inline(always)]
201 fn address(&self) -> ::alloy::primitives::Address {
202 self.address
203 }
204
205 #[inline(always)]
206 fn storage(&mut self) -> &mut crate::storage::StorageCtx {
207 &mut self.storage
208 }
209 }
210 }
211}
212
213pub(crate) fn gen_slots_module(allocated_fields: &[LayoutField<'_>]) -> proc_macro2::TokenStream {
217 let constants = packing::gen_constants_from_ir(allocated_fields, false);
219 let collision_checks = gen_collision_checks(allocated_fields);
220
221 quote! {
222 pub mod slots {
223 use super::*;
224
225 #constants
226 #collision_checks
227 }
228 }
229}
230
231fn gen_collision_checks(allocated_fields: &[LayoutField<'_>]) -> proc_macro2::TokenStream {
233 let mut generated = proc_macro2::TokenStream::new();
234 let mut check_fn_calls = Vec::new();
235
236 for (idx, allocated) in allocated_fields.iter().enumerate() {
238 if let Some((check_fn_name, check_fn)) =
239 packing::gen_collision_check_fn(idx, allocated, allocated_fields)
240 {
241 generated.extend(check_fn);
242 check_fn_calls.push(check_fn_name);
243 }
244 }
245
246 generated.extend(quote! {
249 #[cfg(debug_assertions)]
250 #[inline(always)]
251 pub(super) fn __check_all_collisions() {
252 #(#check_fn_calls();)*
253 }
254 });
255
256 generated
257}
258
259pub(crate) fn gen_default_impl(name: &Ident) -> proc_macro2::TokenStream {
263 quote! {
264 impl ::core::default::Default for #name {
265 fn default() -> Self {
266 Self::new()
267 }
268 }
269 }
270}