1use proc_macro::TokenStream;
6use quote::{ToTokens, format_ident, quote, quote_spanned};
7use syn::{
8 Attribute, BinOp, Data, DataEnum, DeriveInput, Expr, ExprBinary, ExprMacro, Item, ItemMacro,
9 Stmt, StmtMacro, Token, UnOp,
10 fold::{Fold, fold_expr, fold_item_macro, fold_stmt},
11 parse::Parser,
12 parse_macro_input, parse2,
13 punctuated::Punctuated,
14 spanned::Spanned,
15};
16
17#[proc_macro_attribute]
18pub fn init_static_initializers(_args: TokenStream, item: TokenStream) -> TokenStream {
19 let mut input = parse_macro_input!(item as syn::ItemFn);
20
21 let body = &input.block;
22 input.block = syn::parse2(quote! {
23 {
24 std::thread::spawn(|| {
40 use iota_protocol_config::ProtocolConfig;
41 ::iota_simulator::telemetry_subscribers::init_for_testing();
42 ::iota_simulator::iota_types::execution::get_denied_certificates();
43 ::iota_simulator::iota_framework::BuiltInFramework::all_package_ids();
44 ::iota_simulator::iota_types::gas::IotaGasStatus::new_unmetered();
45
46 let mut cache = ::iota_simulator::lru::LruCache::new(1.try_into().unwrap());
50 cache.put(1, 1);
51
52 {
53 use std::path::PathBuf;
56 use iota_simulator::iota_move_build::{BuildConfig, IotaPackageHooks};
57 use iota_simulator::tempfile::TempDir;
58 use iota_simulator::move_package::package_hooks::register_package_hooks;
59
60 register_package_hooks(Box::new(IotaPackageHooks {}));
61 let mut path = PathBuf::from(env!("SIMTEST_STATIC_INIT_MOVE"));
62 let mut build_config = BuildConfig::new_for_testing();
63
64 build_config.config.implicit_dependencies =
69 iota_simulator::iota_move_build::local_implicit_deps_latest();
70 build_config.config.install_dir = Some(TempDir::new().unwrap().keep());
71 let _all_module_bytes = build_config
72 .build(&path)
73 .unwrap()
74 .get_package_bytes(false);
75 }
76
77
78 use ::iota_simulator::anemo_tower::callback::CallbackLayer;
79 use ::iota_simulator::anemo_tower::trace::DefaultMakeSpan;
80 use ::iota_simulator::anemo_tower::trace::DefaultOnFailure;
81 use ::iota_simulator::anemo_tower::trace::TraceLayer;
82 use ::iota_metrics::metrics_network::{NetworkMetrics, MetricsMakeCallbackHandler};
83
84 use std::sync::Arc;
85 use ::iota_simulator::fastcrypto::traits::KeyPair;
86 use ::iota_simulator::rand_crate::rngs::{StdRng, OsRng};
87 use ::iota_simulator::rand::SeedableRng;
88 use ::iota_simulator::tower::ServiceBuilder;
89
90 let rt = ::iota_simulator::runtime::Runtime::new();
93 rt.block_on(async move {
94 use ::iota_simulator::anemo::{Network, Request};
95
96 let make_network = |port: u16| {
97 let registry = prometheus_filtered::Registry::new();
98 let inbound_network_metrics =
99 NetworkMetrics::new("iota", "inbound", ®istry);
100 let outbound_network_metrics =
101 NetworkMetrics::new("iota", "outbound", ®istry);
102
103 let service = ServiceBuilder::new()
104 .layer(
105 TraceLayer::new_for_server_errors()
106 .make_span_with(DefaultMakeSpan::new().level(tracing::Level::INFO))
107 .on_failure(DefaultOnFailure::new().level(tracing::Level::WARN)),
108 )
109 .layer(CallbackLayer::new(MetricsMakeCallbackHandler::new(
110 Arc::new(inbound_network_metrics),
111 usize::MAX,
112 )))
113 .service(::iota_simulator::anemo::Router::new());
114
115 let outbound_layer = ServiceBuilder::new()
116 .layer(
117 TraceLayer::new_for_client_and_server_errors()
118 .make_span_with(DefaultMakeSpan::new().level(tracing::Level::INFO))
119 .on_failure(DefaultOnFailure::new().level(tracing::Level::WARN)),
120 )
121 .layer(CallbackLayer::new(MetricsMakeCallbackHandler::new(
122 Arc::new(outbound_network_metrics),
123 usize::MAX,
124 )))
125 .into_inner();
126
127
128 Network::bind(format!("127.0.0.1:{}", port))
129 .server_name("static-init-network")
130 .private_key(
131 ::iota_simulator::fastcrypto::ed25519::Ed25519KeyPair::generate(&mut StdRng::from_rng(OsRng).unwrap())
132 .private()
133 .0
134 .to_bytes(),
135 )
136 .start(service)
137 .unwrap()
138 };
139 let n1 = make_network(80);
140 let n2 = make_network(81);
141
142 let _peer = n1.connect(n2.local_addr()).await.unwrap();
143 });
144 }).join().unwrap();
145
146 #body
147 }
148 })
149 .expect("Parsing failure");
150
151 let result = quote! {
152 #input
153 };
154
155 result.into()
156}
157
158#[proc_macro_attribute]
164pub fn iota_test(args: TokenStream, item: TokenStream) -> TokenStream {
165 let input = parse_macro_input!(item as syn::ItemFn);
166 let arg_parser = Punctuated::<syn::Meta, Token![,]>::parse_terminated;
167 let args = arg_parser.parse(args).unwrap().into_iter();
168
169 let header = if cfg!(msim) {
170 quote! {
171 #[::iota_simulator::sim_test(crate = "iota_simulator", #(#args)* )]
172 }
173 } else {
174 quote! {
175 #[::tokio::test(#(#args)*)]
176 }
177 };
178
179 let result = quote! {
180 #header
181 #[::iota_macros::init_static_initializers]
182 #input
183 };
184
185 result.into()
186}
187
188#[proc_macro_attribute]
199pub fn sim_test(args: TokenStream, item: TokenStream) -> TokenStream {
200 let input = parse_macro_input!(item as syn::ItemFn);
201 let arg_parser = Punctuated::<syn::Meta, Token![,]>::parse_terminated;
202 let args = arg_parser.parse(args).unwrap().into_iter();
203
204 let ignore = input
205 .attrs
206 .iter()
207 .find(|attr| attr.path().is_ident("ignore"))
208 .map_or(quote! {}, |_| quote! { #[ignore] });
209
210 let result = if cfg!(msim) {
211 let sig = &input.sig;
212 let return_type = &sig.output;
213 let body = &input.block;
214 quote! {
215 #[::iota_simulator::sim_test(crate = "iota_simulator", #(#args),*)]
216 #[::iota_macros::init_static_initializers]
217 #ignore
218 #sig {
219 async fn body_fn() #return_type { #body }
220
221 let ret = body_fn().await;
222
223 ::iota_simulator::task::shutdown_all_nodes();
224
225 tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
228
229 assert_eq!(
230 iota_simulator::NodeLeakDetector::get_current_node_count(),
231 0,
232 "IotaNode leak detected"
233 );
234
235 ret
236 }
237 }
238 } else {
239 let fn_name = &input.sig.ident;
240 let sig = &input.sig;
241 let body = &input.block;
242 quote! {
243 #[expect(clippy::needless_return)]
244 #[tokio::test]
245 #ignore
246 #sig {
247 if std::env::var("IOTA_SKIP_SIMTESTS").is_ok() {
248 println!("not running test {} in `cargo test`: IOTA_SKIP_SIMTESTS is set", stringify!(#fn_name));
249
250 struct Ret;
251
252 impl From<Ret> for () {
253 fn from(_ret: Ret) -> Self {
254 }
255 }
256
257 impl<E> From<Ret> for Result<(), E> {
258 fn from(_ret: Ret) -> Self {
259 Ok(())
260 }
261 }
262
263 return Ret.into();
264 }
265
266 #body
267 }
268 }
269 };
270
271 result.into()
272}
273
274#[proc_macro]
275pub fn checked_arithmetic(input: TokenStream) -> TokenStream {
276 let input_file = CheckArithmetic.fold_file(parse_macro_input!(input));
277
278 let output_items = input_file.items;
279
280 let output = quote! {
281 #(#output_items)*
282 };
283
284 TokenStream::from(output)
285}
286
287#[proc_macro_attribute]
288pub fn with_checked_arithmetic(_attr: TokenStream, item: TokenStream) -> TokenStream {
289 let input_item = parse_macro_input!(item as Item);
290 match input_item {
291 Item::Fn(input_fn) => {
292 let transformed_fn = CheckArithmetic.fold_item_fn(input_fn);
293 TokenStream::from(quote! { #transformed_fn })
294 }
295 Item::Impl(input_impl) => {
296 let transformed_impl = CheckArithmetic.fold_item_impl(input_impl);
297 TokenStream::from(quote! { #transformed_impl })
298 }
299 item => {
300 let transformed_impl = CheckArithmetic.fold_item(item);
301 TokenStream::from(quote! { #transformed_impl })
302 }
303 }
304}
305
306struct CheckArithmetic;
307
308impl CheckArithmetic {
309 fn maybe_skip_macro(&self, attrs: &mut Vec<Attribute>) -> bool {
310 if let Some(idx) = attrs
311 .iter()
312 .position(|attr| attr.path().is_ident("skip_checked_arithmetic"))
313 {
314 attrs.remove(idx);
317 true
318 } else {
319 false
320 }
321 }
322
323 fn process_macro_contents(
324 &mut self,
325 tokens: proc_macro2::TokenStream,
326 ) -> syn::Result<proc_macro2::TokenStream> {
327 let parser = Punctuated::<Expr, Token![,]>::parse_terminated;
329 let Ok(exprs) = parser.parse(tokens.clone().into()) else {
330 return Err(syn::Error::new_spanned(
331 tokens,
332 "could not process macro contents - use #[skip_checked_arithmetic] to skip this macro",
333 ));
334 };
335
336 let folded_exprs = exprs
338 .into_iter()
339 .map(|expr| self.fold_expr(expr))
340 .collect::<Vec<_>>();
341
342 let mut folded_tokens = proc_macro2::TokenStream::new();
344 for (i, folded_expr) in folded_exprs.into_iter().enumerate() {
345 if i > 0 {
346 folded_tokens.extend(std::iter::once::<proc_macro2::TokenTree>(
347 proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone).into(),
348 ));
349 }
350 folded_expr.to_tokens(&mut folded_tokens);
351 }
352
353 Ok(folded_tokens)
354 }
355}
356
357impl Fold for CheckArithmetic {
358 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
359 let stmt = fold_stmt(self, stmt);
360 if let Stmt::Macro(stmt_macro) = stmt {
361 let StmtMacro {
362 mut attrs,
363 mut mac,
364 semi_token,
365 } = stmt_macro;
366
367 if self.maybe_skip_macro(&mut attrs) {
368 Stmt::Macro(StmtMacro {
369 attrs,
370 mac,
371 semi_token,
372 })
373 } else {
374 match self.process_macro_contents(mac.tokens.clone()) {
375 Ok(folded_tokens) => {
376 mac.tokens = folded_tokens;
377 Stmt::Macro(StmtMacro {
378 attrs,
379 mac,
380 semi_token,
381 })
382 }
383 Err(error) => parse2(error.to_compile_error()).unwrap(),
384 }
385 }
386 } else {
387 stmt
388 }
389 }
390
391 fn fold_item_macro(&mut self, mut item_macro: ItemMacro) -> ItemMacro {
392 if !self.maybe_skip_macro(&mut item_macro.attrs) {
393 let err = syn::Error::new_spanned(
394 item_macro.to_token_stream(),
395 "cannot process macros - use #[skip_checked_arithmetic] to skip \
396 processing this macro",
397 );
398
399 return parse2(err.to_compile_error()).unwrap();
400 }
401 fold_item_macro(self, item_macro)
402 }
403
404 fn fold_expr(&mut self, expr: Expr) -> Expr {
405 let span = expr.span();
406 let expr = fold_expr(self, expr);
407 let expr = match expr {
408 Expr::Macro(expr_macro) => {
409 let ExprMacro { mut attrs, mut mac } = expr_macro;
410
411 if self.maybe_skip_macro(&mut attrs) {
412 return Expr::Macro(ExprMacro { attrs, mac });
413 } else {
414 match self.process_macro_contents(mac.tokens.clone()) {
415 Ok(folded_tokens) => {
416 mac.tokens = folded_tokens;
417 let expr_macro = Expr::Macro(ExprMacro { attrs, mac });
418 quote!(#expr_macro)
419 }
420 Err(error) => {
421 return Expr::Verbatim(error.to_compile_error());
422 }
423 }
424 }
425 }
426
427 Expr::Binary(expr_binary) => {
428 let ExprBinary {
429 attrs,
430 mut left,
431 op,
432 mut right,
433 } = expr_binary;
434
435 fn remove_parens(expr: &mut Expr) {
436 if let Expr::Paren(paren) = expr {
437 assert!(paren.attrs.is_empty(), "TODO: attrs on parenthesized");
439 *expr = *paren.expr.clone();
440 }
441 }
442
443 macro_rules! wrap_op {
444 ($left: expr, $right: expr, $method: ident, $span: expr) => {{
445 remove_parens(&mut $left);
448 remove_parens(&mut $right);
449
450 quote_spanned!($span => {
451 let (left, right) = (#left, #right);
454 left.$method(right)
455 .unwrap_or_else(||
456 panic!(
457 "Overflow or underflow in {} {} + {}",
458 stringify!($method),
459 left,
460 right,
461 )
462 )
463 })
464 }};
465 }
466
467 macro_rules! wrap_op_assign {
468 ($left: expr, $right: expr, $method: ident, $span: expr) => {{
469 remove_parens(&mut $left);
472 remove_parens(&mut $right);
473
474 quote_spanned!($span => {
475 let (left, right) = (&mut #left, #right);
478 *left = (*left).$method(right)
479 .unwrap_or_else(||
480 panic!(
481 "Overflow or underflow in {} {} + {}",
482 stringify!($method),
483 *left,
484 right
485 )
486 )
487 })
488 }};
489 }
490
491 match op {
492 BinOp::Add(_) => {
493 wrap_op!(left, right, checked_add, span)
494 }
495 BinOp::Sub(_) => {
496 wrap_op!(left, right, checked_sub, span)
497 }
498 BinOp::Mul(_) => {
499 wrap_op!(left, right, checked_mul, span)
500 }
501 BinOp::Div(_) => {
502 wrap_op!(left, right, checked_div, span)
503 }
504 BinOp::Rem(_) => {
505 wrap_op!(left, right, checked_rem, span)
506 }
507 BinOp::AddAssign(_) => {
508 wrap_op_assign!(left, right, checked_add, span)
509 }
510 BinOp::SubAssign(_) => {
511 wrap_op_assign!(left, right, checked_sub, span)
512 }
513 BinOp::MulAssign(_) => {
514 wrap_op_assign!(left, right, checked_mul, span)
515 }
516 BinOp::DivAssign(_) => {
517 wrap_op_assign!(left, right, checked_div, span)
518 }
519 BinOp::RemAssign(_) => {
520 wrap_op_assign!(left, right, checked_rem, span)
521 }
522 _ => {
523 let expr_binary = ExprBinary {
524 attrs,
525 left,
526 op,
527 right,
528 };
529 quote_spanned!(span => #expr_binary)
530 }
531 }
532 }
533 Expr::Unary(expr_unary) => {
534 let op = &expr_unary.op;
535 let operand = &expr_unary.expr;
536 match op {
537 UnOp::Neg(_) => {
538 quote_spanned!(span => #operand.checked_neg().expect("Overflow or underflow in negation"))
539 }
540 _ => quote_spanned!(span => #expr_unary),
541 }
542 }
543 _ => quote_spanned!(span => #expr),
544 };
545
546 parse2(expr).unwrap()
547 }
548}
549
550#[proc_macro_derive(EnumVariantOrder)]
568pub fn enum_variant_order_derive(input: TokenStream) -> TokenStream {
569 let ast = parse_macro_input!(input as DeriveInput);
570 let name = &ast.ident;
571
572 if let Data::Enum(DataEnum { variants, .. }) = ast.data {
573 let variant_entries = variants
574 .iter()
575 .enumerate()
576 .map(|(index, variant)| {
577 let variant_name = variant.ident.to_string();
578 quote! {
579 map.insert( #index as u64, (#variant_name).to_string());
580 }
581 })
582 .collect::<Vec<_>>();
583
584 let deriv = quote! {
585 impl iota_enum_compat_util::EnumOrderMap for #name {
586 fn order_to_variant_map() -> std::collections::BTreeMap<u64, String > {
587 let mut map = std::collections::BTreeMap::new();
588 #(#variant_entries)*
589 map
590 }
591 }
592 };
593
594 deriv.into()
595 } else {
596 panic!("EnumVariantOrder can only be used with enums.");
597 }
598}
599
600#[proc_macro_attribute]
626pub fn allow_deprecated_for_derives(_attr: TokenStream, item: TokenStream) -> TokenStream {
627 let mut input = parse_macro_input!(item as DeriveInput);
628 let vis = &input.vis;
629 let ident = &input.ident;
630 let mod_name = format_ident!("__allow_deprecated_{}", ident.to_string().to_lowercase());
631
632 let mut deprecated_attrs: Vec<Attribute> = Vec::new();
635 input.attrs.retain(|attr| {
636 if attr.path().is_ident("deprecated") {
637 deprecated_attrs.push(attr.clone());
638 false
639 } else {
640 true
641 }
642 });
643
644 quote! {
645 #[allow(deprecated)]
646 mod #mod_name {
647 use super::*;
648 #input
649 }
650 #(#deprecated_attrs)*
651 #vis use #mod_name::#ident;
652 }
653 .into()
654}