1use std::collections::HashMap;
6
7use super::functions::SOLIDITY_RUNTIME;
8
9use std::collections::{HashSet, VecDeque};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum StateMutability {
14 NonPayable,
16 Payable,
18 View,
20 Pure,
22}
23#[allow(dead_code)]
25#[derive(Debug, Clone)]
26pub struct SolExtDomTree {
27 pub(super) idom: Vec<Option<usize>>,
28 pub(super) children: Vec<Vec<usize>>,
29 pub(super) depth: Vec<usize>,
30}
31impl SolExtDomTree {
32 #[allow(dead_code)]
33 pub fn new(n: usize) -> Self {
34 Self {
35 idom: vec![None; n],
36 children: vec![Vec::new(); n],
37 depth: vec![0; n],
38 }
39 }
40 #[allow(dead_code)]
41 pub fn set_idom(&mut self, node: usize, dom: usize) {
42 if node < self.idom.len() {
43 self.idom[node] = Some(dom);
44 if dom < self.children.len() {
45 self.children[dom].push(node);
46 }
47 self.depth[node] = if dom < self.depth.len() {
48 self.depth[dom] + 1
49 } else {
50 1
51 };
52 }
53 }
54 #[allow(dead_code)]
55 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
56 if a == b {
57 return true;
58 }
59 let n = self.idom.len();
60 for _ in 0..n {
61 match self.idom.get(b).copied().flatten() {
62 None => return false,
63 Some(p) if p == a => return true,
64 Some(p) if p == b => return false,
65 Some(p) => b = p,
66 }
67 }
68 false
69 }
70 #[allow(dead_code)]
71 pub fn children_of(&self, n: usize) -> &[usize] {
72 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
73 }
74 #[allow(dead_code)]
75 pub fn depth_of(&self, n: usize) -> usize {
76 self.depth.get(n).copied().unwrap_or(0)
77 }
78 #[allow(dead_code)]
79 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
80 let n = self.idom.len();
81 for _ in 0..(2 * n) {
82 if a == b {
83 return a;
84 }
85 if self.depth_of(a) > self.depth_of(b) {
86 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
87 } else {
88 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
89 }
90 }
91 0
92 }
93}
94#[derive(Debug, Clone, PartialEq)]
96pub enum SolidityExpr {
97 IntLit(i128),
99 BoolLit(bool),
101 StrLit(String),
103 AddressLit(String),
105 HexLit(String),
107 Var(String),
109 This,
111 MsgSender,
113 MsgValue,
115 MsgData,
117 BlockTimestamp,
119 BlockNumber,
121 BlockBasefee,
123 TxOrigin,
125 GasLeft,
127 FieldAccess(Box<SolidityExpr>, String),
129 Index(Box<SolidityExpr>, Box<SolidityExpr>),
131 Call(Box<SolidityExpr>, Vec<SolidityExpr>),
133 NamedCall(Box<SolidityExpr>, Vec<(String, SolidityExpr)>),
135 Cast(SolidityType, Box<SolidityExpr>),
137 AbiEncode(Vec<SolidityExpr>),
139 AbiEncodePacked(Vec<SolidityExpr>),
141 AbiEncodeWithSelector(Box<SolidityExpr>, Vec<SolidityExpr>),
143 Keccak256(Box<SolidityExpr>),
145 Sha256(Box<SolidityExpr>),
147 Ecrecover(
149 Box<SolidityExpr>,
150 Box<SolidityExpr>,
151 Box<SolidityExpr>,
152 Box<SolidityExpr>,
153 ),
154 BinOp(String, Box<SolidityExpr>, Box<SolidityExpr>),
156 UnaryOp(String, Box<SolidityExpr>),
158 Ternary(Box<SolidityExpr>, Box<SolidityExpr>, Box<SolidityExpr>),
160 New(SolidityType, Vec<SolidityExpr>),
162 Delete(Box<SolidityExpr>),
164 ArrayLit(Vec<SolidityExpr>),
166 TupleLit(Vec<SolidityExpr>),
168 TypeMax(SolidityType),
170 TypeMin(SolidityType),
171 Payable(Box<SolidityExpr>),
173}
174#[derive(Debug, Clone)]
176pub struct SolidityStruct {
177 pub name: String,
178 pub fields: Vec<(SolidityType, String)>,
179 pub doc: Option<String>,
180}
181#[derive(Debug, Clone)]
183pub struct SolidityParam {
184 pub ty: SolidityType,
186 pub location: Option<String>,
188 pub name: String,
190}
191impl SolidityParam {
192 pub fn new(ty: SolidityType, name: impl Into<String>) -> Self {
193 let location = if ty.is_reference_type() {
194 Some("memory".into())
195 } else {
196 None
197 };
198 Self {
199 ty,
200 location,
201 name: name.into(),
202 }
203 }
204 pub fn calldata(ty: SolidityType, name: impl Into<String>) -> Self {
205 Self {
206 ty,
207 location: Some("calldata".into()),
208 name: name.into(),
209 }
210 }
211 pub fn storage(ty: SolidityType, name: impl Into<String>) -> Self {
212 Self {
213 ty,
214 location: Some("storage".into()),
215 name: name.into(),
216 }
217 }
218}
219#[derive(Debug, Clone)]
221pub struct CompilationCtx {
222 pub pragmas: Vec<String>,
224 pub imports: Vec<String>,
226 pub include_runtime: bool,
228}
229#[allow(dead_code)]
230pub struct SolPassRegistry {
231 pub(super) configs: Vec<SolPassConfig>,
232 pub(super) stats: std::collections::HashMap<String, SolPassStats>,
233}
234impl SolPassRegistry {
235 #[allow(dead_code)]
236 pub fn new() -> Self {
237 SolPassRegistry {
238 configs: Vec::new(),
239 stats: std::collections::HashMap::new(),
240 }
241 }
242 #[allow(dead_code)]
243 pub fn register(&mut self, config: SolPassConfig) {
244 self.stats
245 .insert(config.pass_name.clone(), SolPassStats::new());
246 self.configs.push(config);
247 }
248 #[allow(dead_code)]
249 pub fn enabled_passes(&self) -> Vec<&SolPassConfig> {
250 self.configs.iter().filter(|c| c.enabled).collect()
251 }
252 #[allow(dead_code)]
253 pub fn get_stats(&self, name: &str) -> Option<&SolPassStats> {
254 self.stats.get(name)
255 }
256 #[allow(dead_code)]
257 pub fn total_passes(&self) -> usize {
258 self.configs.len()
259 }
260 #[allow(dead_code)]
261 pub fn enabled_count(&self) -> usize {
262 self.enabled_passes().len()
263 }
264 #[allow(dead_code)]
265 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
266 if let Some(stats) = self.stats.get_mut(name) {
267 stats.record_run(changes, time_ms, iter);
268 }
269 }
270}
271#[allow(dead_code)]
273#[derive(Debug, Clone, PartialEq, Eq, Hash)]
274pub enum SolExtPassPhase {
275 Early,
276 Middle,
277 Late,
278 Finalize,
279}
280impl SolExtPassPhase {
281 #[allow(dead_code)]
282 pub fn is_early(&self) -> bool {
283 matches!(self, Self::Early)
284 }
285 #[allow(dead_code)]
286 pub fn is_middle(&self) -> bool {
287 matches!(self, Self::Middle)
288 }
289 #[allow(dead_code)]
290 pub fn is_late(&self) -> bool {
291 matches!(self, Self::Late)
292 }
293 #[allow(dead_code)]
294 pub fn is_finalize(&self) -> bool {
295 matches!(self, Self::Finalize)
296 }
297 #[allow(dead_code)]
298 pub fn order(&self) -> u32 {
299 match self {
300 Self::Early => 0,
301 Self::Middle => 1,
302 Self::Late => 2,
303 Self::Finalize => 3,
304 }
305 }
306 #[allow(dead_code)]
307 pub fn from_order(n: u32) -> Option<Self> {
308 match n {
309 0 => Some(Self::Early),
310 1 => Some(Self::Middle),
311 2 => Some(Self::Late),
312 3 => Some(Self::Finalize),
313 _ => None,
314 }
315 }
316}
317#[allow(dead_code)]
319#[derive(Debug, Clone)]
320pub struct SolExtWorklist {
321 pub(super) items: std::collections::VecDeque<usize>,
322 pub(super) present: Vec<bool>,
323}
324impl SolExtWorklist {
325 #[allow(dead_code)]
326 pub fn new(capacity: usize) -> Self {
327 Self {
328 items: std::collections::VecDeque::new(),
329 present: vec![false; capacity],
330 }
331 }
332 #[allow(dead_code)]
333 pub fn push(&mut self, id: usize) {
334 if id < self.present.len() && !self.present[id] {
335 self.present[id] = true;
336 self.items.push_back(id);
337 }
338 }
339 #[allow(dead_code)]
340 pub fn push_front(&mut self, id: usize) {
341 if id < self.present.len() && !self.present[id] {
342 self.present[id] = true;
343 self.items.push_front(id);
344 }
345 }
346 #[allow(dead_code)]
347 pub fn pop(&mut self) -> Option<usize> {
348 let id = self.items.pop_front()?;
349 if id < self.present.len() {
350 self.present[id] = false;
351 }
352 Some(id)
353 }
354 #[allow(dead_code)]
355 pub fn is_empty(&self) -> bool {
356 self.items.is_empty()
357 }
358 #[allow(dead_code)]
359 pub fn len(&self) -> usize {
360 self.items.len()
361 }
362 #[allow(dead_code)]
363 pub fn contains(&self, id: usize) -> bool {
364 id < self.present.len() && self.present[id]
365 }
366 #[allow(dead_code)]
367 pub fn drain_all(&mut self) -> Vec<usize> {
368 let v: Vec<usize> = self.items.drain(..).collect();
369 for &id in &v {
370 if id < self.present.len() {
371 self.present[id] = false;
372 }
373 }
374 v
375 }
376}
377#[derive(Debug, Clone)]
379pub struct SolidityEnum {
380 pub name: String,
381 pub variants: Vec<String>,
382 pub doc: Option<String>,
383}
384#[derive(Debug, Clone)]
386pub struct SolidityStateVar {
387 pub ty: SolidityType,
388 pub name: String,
389 pub visibility: Visibility,
390 pub is_immutable: bool,
391 pub is_constant: bool,
392 pub init: Option<SolidityExpr>,
393 pub doc: Option<String>,
394}
395#[derive(Debug, Default)]
397pub struct SolidityBackend {
398 pub contracts: Vec<SolidityContract>,
400 pub ctx: CompilationCtx,
402 pub type_aliases: HashMap<String, SolidityType>,
404 pub source: String,
406}
407impl SolidityBackend {
408 pub fn new() -> Self {
409 Self::default()
410 }
411 pub fn with_runtime(mut self) -> Self {
412 self.ctx.include_runtime = true;
413 self
414 }
415 pub fn add_contract(&mut self, contract: SolidityContract) {
416 self.contracts.push(contract);
417 }
418 pub fn add_pragma(&mut self, pragma: impl Into<String>) {
419 self.ctx.pragmas.push(pragma.into());
420 }
421 pub fn add_import(&mut self, import: impl Into<String>) {
422 self.ctx.imports.push(import.into());
423 }
424 pub fn compile_decl(&mut self, name: &str, ty: SolidityType) -> SolidityStateVar {
426 SolidityStateVar {
427 ty,
428 name: name.into(),
429 visibility: Visibility::Private,
430 is_immutable: false,
431 is_constant: false,
432 init: None,
433 doc: None,
434 }
435 }
436 pub fn emit_contract(&mut self) -> String {
438 let mut out = String::new();
439 out.push_str("// SPDX-License-Identifier: MIT\n");
440 out.push_str("// Generated by OxiLean Solidity Backend\n\n");
441 for pragma in &self.ctx.pragmas {
442 out.push_str(&format!("pragma solidity {};\n", pragma));
443 }
444 out.push('\n');
445 for imp in &self.ctx.imports {
446 out.push_str(&format!("import \"{}\";\n", imp));
447 }
448 if !self.ctx.imports.is_empty() {
449 out.push('\n');
450 }
451 if self.ctx.include_runtime {
452 out.push_str(SOLIDITY_RUNTIME);
453 out.push('\n');
454 }
455 for contract in &self.contracts {
456 out.push_str(&Self::emit_single_contract(contract));
457 out.push('\n');
458 }
459 self.source = out.clone();
460 out
461 }
462 pub(super) fn emit_single_contract(c: &SolidityContract) -> String {
463 let mut out = String::new();
464 if let Some(doc) = &c.doc {
465 out.push_str(&format!("/// @title {}\n", doc));
466 }
467 let bases = if c.bases.is_empty() {
468 String::new()
469 } else {
470 format!(" is {}", c.bases.join(", "))
471 };
472 out.push_str(&format!("{} {}{} {{\n", c.kind, c.name, bases));
473 for s in &c.structs {
474 out.push_str(&Self::emit_struct(s, 1));
475 }
476 for e in &c.enums {
477 out.push_str(&Self::emit_enum(e, 1));
478 }
479 for ev in &c.events {
480 out.push_str(&Self::emit_event(ev, 1));
481 }
482 for err in &c.errors {
483 out.push_str(&Self::emit_error(err, 1));
484 }
485 for sv in &c.state_vars {
486 out.push_str(&Self::emit_state_var(sv, 1));
487 }
488 if let Some(ctor) = &c.constructor {
489 out.push_str(&Self::emit_constructor(ctor, 1));
490 }
491 if let Some(recv) = &c.receive {
492 out.push_str(&Self::emit_receive(recv, 1));
493 }
494 if let Some(fb) = &c.fallback {
495 out.push_str(&Self::emit_fallback(fb, 1));
496 }
497 for m in &c.modifiers {
498 out.push_str(&Self::emit_modifier(m, 1));
499 }
500 for func in &c.functions {
501 out.push_str(&Self::emit_function(func, 1));
502 }
503 out.push_str("}\n");
504 out
505 }
506 pub(super) fn indent(level: usize) -> String {
507 " ".repeat(level)
508 }
509 pub(super) fn emit_struct(s: &SolidityStruct, indent: usize) -> String {
510 let ind = Self::indent(indent);
511 let mut out = String::new();
512 if let Some(doc) = &s.doc {
513 out.push_str(&format!("{}/// @dev {}\n", ind, doc));
514 }
515 out.push_str(&format!("{}struct {} {{\n", ind, s.name));
516 for (ty, name) in &s.fields {
517 out.push_str(&format!("{} {} {};\n", ind, ty, name));
518 }
519 out.push_str(&format!("{}}}\n", ind));
520 out
521 }
522 pub(super) fn emit_enum(e: &SolidityEnum, indent: usize) -> String {
523 let ind = Self::indent(indent);
524 let mut out = String::new();
525 if let Some(doc) = &e.doc {
526 out.push_str(&format!("{}/// @dev {}\n", ind, doc));
527 }
528 out.push_str(&format!(
529 "{}enum {} {{ {} }}\n",
530 ind,
531 e.name,
532 e.variants.join(", ")
533 ));
534 out
535 }
536 pub(super) fn emit_event(ev: &SolidityEvent, indent: usize) -> String {
537 let ind = Self::indent(indent);
538 let mut out = String::new();
539 if let Some(doc) = &ev.doc {
540 out.push_str(&format!("{}/// @dev {}\n", ind, doc));
541 }
542 let fields: Vec<String> = ev
543 .fields
544 .iter()
545 .map(|(ty, indexed, name)| {
546 if *indexed {
547 format!("{} indexed {}", ty, name)
548 } else {
549 format!("{} {}", ty, name)
550 }
551 })
552 .collect();
553 let anon = if ev.anonymous { " anonymous" } else { "" };
554 out.push_str(&format!(
555 "{}event {}({}){};\n",
556 ind,
557 ev.name,
558 fields.join(", "),
559 anon
560 ));
561 out
562 }
563 pub(super) fn emit_error(err: &SolidityError, indent: usize) -> String {
564 let ind = Self::indent(indent);
565 let params: Vec<String> = err.params.iter().map(|p| p.to_string()).collect();
566 format!("{}error {}({});\n", ind, err.name, params.join(", "))
567 }
568 pub(super) fn emit_state_var(sv: &SolidityStateVar, indent: usize) -> String {
569 let ind = Self::indent(indent);
570 let mut out = String::new();
571 if let Some(doc) = &sv.doc {
572 out.push_str(&format!("{}/// @dev {}\n", ind, doc));
573 }
574 let mut parts = vec![sv.ty.to_string(), sv.visibility.to_string()];
575 if sv.is_constant {
576 parts.push("constant".into());
577 } else if sv.is_immutable {
578 parts.push("immutable".into());
579 }
580 parts.push(sv.name.clone());
581 if let Some(init) = &sv.init {
582 out.push_str(&format!("{}{} = {};\n", ind, parts.join(" "), init));
583 } else {
584 out.push_str(&format!("{}{};\n", ind, parts.join(" ")));
585 }
586 out
587 }
588 pub(super) fn emit_constructor(ctor: &SolidityFunction, indent: usize) -> String {
589 let ind = Self::indent(indent);
590 let params: Vec<String> = ctor.params.iter().map(|p| p.to_string()).collect();
591 let mut header = format!("{}constructor({})", ind, params.join(", "));
592 let mut muts = String::new();
593 let m_str = ctor.mutability.to_string();
594 if !m_str.is_empty() {
595 muts.push(' ');
596 muts.push_str(&m_str);
597 }
598 header.push_str(&muts);
599 Self::emit_fn_body(&header, &ctor.body, indent)
600 }
601 pub(super) fn emit_receive(recv: &SolidityFunction, indent: usize) -> String {
602 let ind = Self::indent(indent);
603 let header = format!("{}receive() external payable", ind);
604 Self::emit_fn_body(&header, &recv.body, indent)
605 }
606 pub(super) fn emit_fallback(fb: &SolidityFunction, indent: usize) -> String {
607 let ind = Self::indent(indent);
608 let header = format!("{}fallback() external payable", ind);
609 Self::emit_fn_body(&header, &fb.body, indent)
610 }
611 pub(super) fn emit_modifier(m: &SolidityModifier, indent: usize) -> String {
612 let ind = Self::indent(indent);
613 let params: Vec<String> = m.params.iter().map(|p| p.to_string()).collect();
614 let header = format!("{}modifier {}({})", ind, m.name, params.join(", "));
615 Self::emit_fn_body(&header, &m.body, indent)
616 }
617 pub(super) fn emit_function(func: &SolidityFunction, indent: usize) -> String {
618 let ind = Self::indent(indent);
619 let mut out = String::new();
620 if let Some(doc) = &func.doc {
621 out.push_str(&format!("{}/// @notice {}\n", ind, doc));
622 }
623 let params: Vec<String> = func.params.iter().map(|p| p.to_string()).collect();
624 let mut header = format!(
625 "{}function {}({}) {} {}",
626 ind,
627 func.name,
628 params.join(", "),
629 func.visibility,
630 func.mutability
631 );
632 header = header.trim_end().to_string();
633 if func.is_virtual {
634 header.push_str(" virtual");
635 }
636 if func.is_override {
637 header.push_str(" override");
638 }
639 for (mod_name, mod_args) in &func.modifiers {
640 if mod_args.is_empty() {
641 header.push_str(&format!(" {}", mod_name));
642 } else {
643 let args: Vec<String> = mod_args.iter().map(|a| a.to_string()).collect();
644 header.push_str(&format!(" {}({})", mod_name, args.join(", ")));
645 }
646 }
647 if !func.returns.is_empty() {
648 let rets: Vec<String> = func.returns.iter().map(|p| p.to_string()).collect();
649 header.push_str(&format!(" returns ({})", rets.join(", ")));
650 }
651 if func.body.is_empty() {
652 out.push_str(&format!("{};\n", header));
653 } else {
654 out.push_str(&Self::emit_fn_body(&header, &func.body, indent));
655 }
656 out
657 }
658 pub(super) fn emit_fn_body(header: &str, body: &[SolidityStmt], indent: usize) -> String {
659 let mut out = format!("{} {{\n", header);
660 for stmt in body {
661 out.push_str(&Self::emit_stmt(stmt, indent + 1));
662 }
663 out.push_str(&format!("{}}}\n", Self::indent(indent)));
664 out
665 }
666 pub(super) fn emit_stmt(stmt: &SolidityStmt, indent: usize) -> String {
667 let ind = Self::indent(indent);
668 match stmt {
669 SolidityStmt::VarDecl {
670 ty,
671 location,
672 name,
673 init,
674 } => {
675 let loc_str = location.as_deref().unwrap_or("");
676 let loc_part = if loc_str.is_empty() {
677 String::new()
678 } else {
679 format!(" {}", loc_str)
680 };
681 if let Some(expr) = init {
682 format!("{}{}{} {} = {};\n", ind, ty, loc_part, name, expr)
683 } else {
684 format!("{}{}{} {};\n", ind, ty, loc_part, name)
685 }
686 }
687 SolidityStmt::Assign(lhs, rhs) => format!("{}{} = {};\n", ind, lhs, rhs),
688 SolidityStmt::CompoundAssign(op, lhs, rhs) => {
689 format!("{}{} {}= {};\n", ind, lhs, op, rhs)
690 }
691 SolidityStmt::ExprStmt(expr) => format!("{}{};\n", ind, expr),
692 SolidityStmt::Return(None) => format!("{}return;\n", ind),
693 SolidityStmt::Return(Some(expr)) => format!("{}return {};\n", ind, expr),
694 SolidityStmt::If(cond, then_stmts, else_stmts) => {
695 let mut out = format!("{}if ({}) {{\n", ind, cond);
696 for s in then_stmts {
697 out.push_str(&Self::emit_stmt(s, indent + 1));
698 }
699 if else_stmts.is_empty() {
700 out.push_str(&format!("{}}}\n", ind));
701 } else {
702 out.push_str(&format!("{}}} else {{\n", ind));
703 for s in else_stmts {
704 out.push_str(&Self::emit_stmt(s, indent + 1));
705 }
706 out.push_str(&format!("{}}}\n", ind));
707 }
708 out
709 }
710 SolidityStmt::While(cond, body) => {
711 let mut out = format!("{}while ({}) {{\n", ind, cond);
712 for s in body {
713 out.push_str(&Self::emit_stmt(s, indent + 1));
714 }
715 out.push_str(&format!("{}}}\n", ind));
716 out
717 }
718 SolidityStmt::For(init, cond, update, body) => {
719 let init_str = init
720 .as_ref()
721 .map(|s| {
722 Self::emit_stmt(s, 0)
723 .trim_end_matches('\n')
724 .trim_end_matches(';')
725 .to_string()
726 })
727 .unwrap_or_default();
728 let cond_str = cond.as_ref().map(|e| e.to_string()).unwrap_or_default();
729 let upd_str = update
730 .as_ref()
731 .map(|s| {
732 Self::emit_stmt(s, 0)
733 .trim_end_matches('\n')
734 .trim_end_matches(';')
735 .to_string()
736 })
737 .unwrap_or_default();
738 let mut out = format!("{}for ({}; {}; {}) {{\n", ind, init_str, cond_str, upd_str);
739 for s in body {
740 out.push_str(&Self::emit_stmt(s, indent + 1));
741 }
742 out.push_str(&format!("{}}}\n", ind));
743 out
744 }
745 SolidityStmt::DoWhile(body, cond) => {
746 let mut out = format!("{}do {{\n", ind);
747 for s in body {
748 out.push_str(&Self::emit_stmt(s, indent + 1));
749 }
750 out.push_str(&format!("{}}} while ({});\n", ind, cond));
751 out
752 }
753 SolidityStmt::Emit(name, args) => {
754 let args_str: Vec<String> = args.iter().map(|a| a.to_string()).collect();
755 format!("{}emit {}({});\n", ind, name, args_str.join(", "))
756 }
757 SolidityStmt::Revert(name, args) => {
758 let args_str: Vec<String> = args.iter().map(|a| a.to_string()).collect();
759 format!("{}revert {}({});\n", ind, name, args_str.join(", "))
760 }
761 SolidityStmt::Require(cond, msg) => {
762 if let Some(m) = msg {
763 format!("{}require({}, \"{}\");\n", ind, cond, m)
764 } else {
765 format!("{}require({});\n", ind, cond)
766 }
767 }
768 SolidityStmt::Assert(cond) => format!("{}assert({});\n", ind, cond),
769 SolidityStmt::Break => format!("{}break;\n", ind),
770 SolidityStmt::Continue => format!("{}continue;\n", ind),
771 SolidityStmt::Unchecked(stmts) => {
772 let mut out = format!("{}unchecked {{\n", ind);
773 for s in stmts {
774 out.push_str(&Self::emit_stmt(s, indent + 1));
775 }
776 out.push_str(&format!("{}}}\n", ind));
777 out
778 }
779 SolidityStmt::Assembly(body) => {
780 format!("{}assembly {{\n{}{}}}\n", ind, body, ind)
781 }
782 SolidityStmt::MultiAssign(lhs, rhs) => {
783 let lhs_str: Vec<String> = lhs.iter().map(|e| e.to_string()).collect();
784 format!("{}({}) = {};\n", ind, lhs_str.join(", "), rhs)
785 }
786 SolidityStmt::Block(stmts) => {
787 let mut out = format!("{}{{\n", ind);
788 for s in stmts {
789 out.push_str(&Self::emit_stmt(s, indent + 1));
790 }
791 out.push_str(&format!("{}}}\n", ind));
792 out
793 }
794 }
795 }
796}
797#[derive(Debug, Clone)]
799pub struct SolidityError {
800 pub name: String,
801 pub params: Vec<SolidityParam>,
802 pub doc: Option<String>,
803}
804#[allow(dead_code)]
805#[derive(Debug, Clone)]
806pub struct SolLivenessInfo {
807 pub live_in: Vec<std::collections::HashSet<u32>>,
808 pub live_out: Vec<std::collections::HashSet<u32>>,
809 pub defs: Vec<std::collections::HashSet<u32>>,
810 pub uses: Vec<std::collections::HashSet<u32>>,
811}
812impl SolLivenessInfo {
813 #[allow(dead_code)]
814 pub fn new(block_count: usize) -> Self {
815 SolLivenessInfo {
816 live_in: vec![std::collections::HashSet::new(); block_count],
817 live_out: vec![std::collections::HashSet::new(); block_count],
818 defs: vec![std::collections::HashSet::new(); block_count],
819 uses: vec![std::collections::HashSet::new(); block_count],
820 }
821 }
822 #[allow(dead_code)]
823 pub fn add_def(&mut self, block: usize, var: u32) {
824 if block < self.defs.len() {
825 self.defs[block].insert(var);
826 }
827 }
828 #[allow(dead_code)]
829 pub fn add_use(&mut self, block: usize, var: u32) {
830 if block < self.uses.len() {
831 self.uses[block].insert(var);
832 }
833 }
834 #[allow(dead_code)]
835 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
836 self.live_in
837 .get(block)
838 .map(|s| s.contains(&var))
839 .unwrap_or(false)
840 }
841 #[allow(dead_code)]
842 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
843 self.live_out
844 .get(block)
845 .map(|s| s.contains(&var))
846 .unwrap_or(false)
847 }
848}
849#[allow(dead_code)]
850#[derive(Debug, Clone)]
851pub struct SolCacheEntry {
852 pub key: String,
853 pub data: Vec<u8>,
854 pub timestamp: u64,
855 pub valid: bool,
856}
857#[allow(dead_code)]
859#[derive(Debug)]
860pub struct SolExtCache {
861 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
862 pub(super) cap: usize,
863 pub(super) total_hits: u64,
864 pub(super) total_misses: u64,
865}
866impl SolExtCache {
867 #[allow(dead_code)]
868 pub fn new(cap: usize) -> Self {
869 Self {
870 entries: Vec::new(),
871 cap,
872 total_hits: 0,
873 total_misses: 0,
874 }
875 }
876 #[allow(dead_code)]
877 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
878 for e in self.entries.iter_mut() {
879 if e.0 == key && e.2 {
880 e.3 += 1;
881 self.total_hits += 1;
882 return Some(&e.1);
883 }
884 }
885 self.total_misses += 1;
886 None
887 }
888 #[allow(dead_code)]
889 pub fn put(&mut self, key: u64, data: Vec<u8>) {
890 if self.entries.len() >= self.cap {
891 self.entries.retain(|e| e.2);
892 if self.entries.len() >= self.cap {
893 self.entries.remove(0);
894 }
895 }
896 self.entries.push((key, data, true, 0));
897 }
898 #[allow(dead_code)]
899 pub fn invalidate(&mut self) {
900 for e in self.entries.iter_mut() {
901 e.2 = false;
902 }
903 }
904 #[allow(dead_code)]
905 pub fn hit_rate(&self) -> f64 {
906 let t = self.total_hits + self.total_misses;
907 if t == 0 {
908 0.0
909 } else {
910 self.total_hits as f64 / t as f64
911 }
912 }
913 #[allow(dead_code)]
914 pub fn live_count(&self) -> usize {
915 self.entries.iter().filter(|e| e.2).count()
916 }
917}
918#[allow(dead_code)]
919#[derive(Debug, Clone)]
920pub struct SolDepGraph {
921 pub(super) nodes: Vec<u32>,
922 pub(super) edges: Vec<(u32, u32)>,
923}
924impl SolDepGraph {
925 #[allow(dead_code)]
926 pub fn new() -> Self {
927 SolDepGraph {
928 nodes: Vec::new(),
929 edges: Vec::new(),
930 }
931 }
932 #[allow(dead_code)]
933 pub fn add_node(&mut self, id: u32) {
934 if !self.nodes.contains(&id) {
935 self.nodes.push(id);
936 }
937 }
938 #[allow(dead_code)]
939 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
940 self.add_node(dep);
941 self.add_node(dependent);
942 self.edges.push((dep, dependent));
943 }
944 #[allow(dead_code)]
945 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
946 self.edges
947 .iter()
948 .filter(|(d, _)| *d == node)
949 .map(|(_, dep)| *dep)
950 .collect()
951 }
952 #[allow(dead_code)]
953 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
954 self.edges
955 .iter()
956 .filter(|(_, dep)| *dep == node)
957 .map(|(d, _)| *d)
958 .collect()
959 }
960 #[allow(dead_code)]
961 pub fn topological_sort(&self) -> Vec<u32> {
962 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
963 for &n in &self.nodes {
964 in_degree.insert(n, 0);
965 }
966 for (_, dep) in &self.edges {
967 *in_degree.entry(*dep).or_insert(0) += 1;
968 }
969 let mut queue: std::collections::VecDeque<u32> = self
970 .nodes
971 .iter()
972 .filter(|&&n| in_degree[&n] == 0)
973 .copied()
974 .collect();
975 let mut result = Vec::new();
976 while let Some(node) = queue.pop_front() {
977 result.push(node);
978 for dep in self.dependents_of(node) {
979 let cnt = in_degree.entry(dep).or_insert(0);
980 *cnt = cnt.saturating_sub(1);
981 if *cnt == 0 {
982 queue.push_back(dep);
983 }
984 }
985 }
986 result
987 }
988 #[allow(dead_code)]
989 pub fn has_cycle(&self) -> bool {
990 self.topological_sort().len() < self.nodes.len()
991 }
992}
993#[derive(Debug, Clone, PartialEq, Eq, Hash)]
995pub enum SolidityType {
996 Uint256,
998 Uint128,
1000 Uint64,
1002 Uint32,
1004 Uint8,
1006 Int256,
1008 Int128,
1010 Int64,
1012 Int32,
1014 Int8,
1016 Address,
1018 AddressPayable,
1020 Bool,
1022 Bytes,
1024 Bytes32,
1026 Bytes16,
1028 Bytes4,
1030 StringTy,
1032 Mapping(Box<SolidityType>, Box<SolidityType>),
1034 DynArray(Box<SolidityType>),
1036 FixedArray(Box<SolidityType>, usize),
1038 Named(String),
1040 Tuple(Vec<SolidityType>),
1042}
1043impl SolidityType {
1044 pub fn is_reference_type(&self) -> bool {
1046 matches!(
1047 self,
1048 SolidityType::Bytes
1049 | SolidityType::StringTy
1050 | SolidityType::Mapping(_, _)
1051 | SolidityType::DynArray(_)
1052 | SolidityType::FixedArray(_, _)
1053 | SolidityType::Named(_)
1054 | SolidityType::Tuple(_)
1055 )
1056 }
1057 pub fn default_param_location(&self) -> &'static str {
1059 if self.is_reference_type() {
1060 "memory"
1061 } else {
1062 ""
1063 }
1064 }
1065 pub fn abi_canonical(&self) -> String {
1067 match self {
1068 SolidityType::Uint256 => "uint256".into(),
1069 SolidityType::Uint128 => "uint128".into(),
1070 SolidityType::Uint64 => "uint64".into(),
1071 SolidityType::Uint32 => "uint32".into(),
1072 SolidityType::Uint8 => "uint8".into(),
1073 SolidityType::Int256 => "int256".into(),
1074 SolidityType::Int128 => "int128".into(),
1075 SolidityType::Int64 => "int64".into(),
1076 SolidityType::Int32 => "int32".into(),
1077 SolidityType::Int8 => "int8".into(),
1078 SolidityType::Address | SolidityType::AddressPayable => "address".into(),
1079 SolidityType::Bool => "bool".into(),
1080 SolidityType::Bytes => "bytes".into(),
1081 SolidityType::Bytes32 => "bytes32".into(),
1082 SolidityType::Bytes16 => "bytes16".into(),
1083 SolidityType::Bytes4 => "bytes4".into(),
1084 SolidityType::StringTy => "string".into(),
1085 SolidityType::Mapping(k, v) => {
1086 format!("mapping({},{})", k.abi_canonical(), v.abi_canonical())
1087 }
1088 SolidityType::DynArray(elem) => format!("{}[]", elem.abi_canonical()),
1089 SolidityType::FixedArray(elem, n) => {
1090 format!("{}[{}]", elem.abi_canonical(), n)
1091 }
1092 SolidityType::Named(name) => name.clone(),
1093 SolidityType::Tuple(elems) => {
1094 let inner: Vec<String> = elems.iter().map(|t| t.abi_canonical()).collect();
1095 format!("({})", inner.join(","))
1096 }
1097 }
1098 }
1099}
1100#[allow(dead_code)]
1101#[derive(Debug, Clone, Default)]
1102pub struct SolPassStats {
1103 pub total_runs: u32,
1104 pub successful_runs: u32,
1105 pub total_changes: u64,
1106 pub time_ms: u64,
1107 pub iterations_used: u32,
1108}
1109impl SolPassStats {
1110 #[allow(dead_code)]
1111 pub fn new() -> Self {
1112 Self::default()
1113 }
1114 #[allow(dead_code)]
1115 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1116 self.total_runs += 1;
1117 self.successful_runs += 1;
1118 self.total_changes += changes;
1119 self.time_ms += time_ms;
1120 self.iterations_used = iterations;
1121 }
1122 #[allow(dead_code)]
1123 pub fn average_changes_per_run(&self) -> f64 {
1124 if self.total_runs == 0 {
1125 return 0.0;
1126 }
1127 self.total_changes as f64 / self.total_runs as f64
1128 }
1129 #[allow(dead_code)]
1130 pub fn success_rate(&self) -> f64 {
1131 if self.total_runs == 0 {
1132 return 0.0;
1133 }
1134 self.successful_runs as f64 / self.total_runs as f64
1135 }
1136 #[allow(dead_code)]
1137 pub fn format_summary(&self) -> String {
1138 format!(
1139 "Runs: {}/{}, Changes: {}, Time: {}ms",
1140 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1141 )
1142 }
1143}
1144#[allow(dead_code)]
1145#[derive(Debug, Clone)]
1146pub struct SolDominatorTree {
1147 pub idom: Vec<Option<u32>>,
1148 pub dom_children: Vec<Vec<u32>>,
1149 pub dom_depth: Vec<u32>,
1150}
1151impl SolDominatorTree {
1152 #[allow(dead_code)]
1153 pub fn new(size: usize) -> Self {
1154 SolDominatorTree {
1155 idom: vec![None; size],
1156 dom_children: vec![Vec::new(); size],
1157 dom_depth: vec![0; size],
1158 }
1159 }
1160 #[allow(dead_code)]
1161 pub fn set_idom(&mut self, node: usize, idom: u32) {
1162 self.idom[node] = Some(idom);
1163 }
1164 #[allow(dead_code)]
1165 pub fn dominates(&self, a: usize, b: usize) -> bool {
1166 if a == b {
1167 return true;
1168 }
1169 let mut cur = b;
1170 loop {
1171 match self.idom[cur] {
1172 Some(parent) if parent as usize == a => return true,
1173 Some(parent) if parent as usize == cur => return false,
1174 Some(parent) => cur = parent as usize,
1175 None => return false,
1176 }
1177 }
1178 }
1179 #[allow(dead_code)]
1180 pub fn depth(&self, node: usize) -> u32 {
1181 self.dom_depth.get(node).copied().unwrap_or(0)
1182 }
1183}
1184#[allow(dead_code)]
1185#[derive(Debug, Clone)]
1186pub struct SolPassConfig {
1187 pub phase: SolPassPhase,
1188 pub enabled: bool,
1189 pub max_iterations: u32,
1190 pub debug_output: bool,
1191 pub pass_name: String,
1192}
1193impl SolPassConfig {
1194 #[allow(dead_code)]
1195 pub fn new(name: impl Into<String>, phase: SolPassPhase) -> Self {
1196 SolPassConfig {
1197 phase,
1198 enabled: true,
1199 max_iterations: 10,
1200 debug_output: false,
1201 pass_name: name.into(),
1202 }
1203 }
1204 #[allow(dead_code)]
1205 pub fn disabled(mut self) -> Self {
1206 self.enabled = false;
1207 self
1208 }
1209 #[allow(dead_code)]
1210 pub fn with_debug(mut self) -> Self {
1211 self.debug_output = true;
1212 self
1213 }
1214 #[allow(dead_code)]
1215 pub fn max_iter(mut self, n: u32) -> Self {
1216 self.max_iterations = n;
1217 self
1218 }
1219}
1220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1222pub enum Visibility {
1223 Public,
1224 Private,
1225 Internal,
1226 External,
1227}
1228#[allow(dead_code)]
1229#[derive(Debug, Clone, PartialEq)]
1230pub enum SolPassPhase {
1231 Analysis,
1232 Transformation,
1233 Verification,
1234 Cleanup,
1235}
1236impl SolPassPhase {
1237 #[allow(dead_code)]
1238 pub fn name(&self) -> &str {
1239 match self {
1240 SolPassPhase::Analysis => "analysis",
1241 SolPassPhase::Transformation => "transformation",
1242 SolPassPhase::Verification => "verification",
1243 SolPassPhase::Cleanup => "cleanup",
1244 }
1245 }
1246 #[allow(dead_code)]
1247 pub fn is_modifying(&self) -> bool {
1248 matches!(self, SolPassPhase::Transformation | SolPassPhase::Cleanup)
1249 }
1250}
1251#[allow(dead_code)]
1252#[derive(Debug, Clone)]
1253pub struct SolAnalysisCache {
1254 pub(super) entries: std::collections::HashMap<String, SolCacheEntry>,
1255 pub(super) max_size: usize,
1256 pub(super) hits: u64,
1257 pub(super) misses: u64,
1258}
1259impl SolAnalysisCache {
1260 #[allow(dead_code)]
1261 pub fn new(max_size: usize) -> Self {
1262 SolAnalysisCache {
1263 entries: std::collections::HashMap::new(),
1264 max_size,
1265 hits: 0,
1266 misses: 0,
1267 }
1268 }
1269 #[allow(dead_code)]
1270 pub fn get(&mut self, key: &str) -> Option<&SolCacheEntry> {
1271 if self.entries.contains_key(key) {
1272 self.hits += 1;
1273 self.entries.get(key)
1274 } else {
1275 self.misses += 1;
1276 None
1277 }
1278 }
1279 #[allow(dead_code)]
1280 pub fn insert(&mut self, key: String, data: Vec<u8>) {
1281 if self.entries.len() >= self.max_size {
1282 if let Some(oldest) = self.entries.keys().next().cloned() {
1283 self.entries.remove(&oldest);
1284 }
1285 }
1286 self.entries.insert(
1287 key.clone(),
1288 SolCacheEntry {
1289 key,
1290 data,
1291 timestamp: 0,
1292 valid: true,
1293 },
1294 );
1295 }
1296 #[allow(dead_code)]
1297 pub fn invalidate(&mut self, key: &str) {
1298 if let Some(entry) = self.entries.get_mut(key) {
1299 entry.valid = false;
1300 }
1301 }
1302 #[allow(dead_code)]
1303 pub fn clear(&mut self) {
1304 self.entries.clear();
1305 }
1306 #[allow(dead_code)]
1307 pub fn hit_rate(&self) -> f64 {
1308 let total = self.hits + self.misses;
1309 if total == 0 {
1310 return 0.0;
1311 }
1312 self.hits as f64 / total as f64
1313 }
1314 #[allow(dead_code)]
1315 pub fn size(&self) -> usize {
1316 self.entries.len()
1317 }
1318}
1319#[allow(dead_code)]
1321#[derive(Debug, Clone)]
1322pub struct SolExtPassConfig {
1323 pub name: String,
1324 pub phase: SolExtPassPhase,
1325 pub enabled: bool,
1326 pub max_iterations: usize,
1327 pub debug: u32,
1328 pub timeout_ms: Option<u64>,
1329}
1330impl SolExtPassConfig {
1331 #[allow(dead_code)]
1332 pub fn new(name: impl Into<String>) -> Self {
1333 Self {
1334 name: name.into(),
1335 phase: SolExtPassPhase::Middle,
1336 enabled: true,
1337 max_iterations: 100,
1338 debug: 0,
1339 timeout_ms: None,
1340 }
1341 }
1342 #[allow(dead_code)]
1343 pub fn with_phase(mut self, phase: SolExtPassPhase) -> Self {
1344 self.phase = phase;
1345 self
1346 }
1347 #[allow(dead_code)]
1348 pub fn with_max_iter(mut self, n: usize) -> Self {
1349 self.max_iterations = n;
1350 self
1351 }
1352 #[allow(dead_code)]
1353 pub fn with_debug(mut self, d: u32) -> Self {
1354 self.debug = d;
1355 self
1356 }
1357 #[allow(dead_code)]
1358 pub fn disabled(mut self) -> Self {
1359 self.enabled = false;
1360 self
1361 }
1362 #[allow(dead_code)]
1363 pub fn with_timeout(mut self, ms: u64) -> Self {
1364 self.timeout_ms = Some(ms);
1365 self
1366 }
1367 #[allow(dead_code)]
1368 pub fn is_debug_enabled(&self) -> bool {
1369 self.debug > 0
1370 }
1371}
1372#[allow(dead_code)]
1374#[derive(Debug, Clone, Default)]
1375pub struct SolExtPassStats {
1376 pub iterations: usize,
1377 pub changed: bool,
1378 pub nodes_visited: usize,
1379 pub nodes_modified: usize,
1380 pub time_ms: u64,
1381 pub memory_bytes: usize,
1382 pub errors: usize,
1383}
1384impl SolExtPassStats {
1385 #[allow(dead_code)]
1386 pub fn new() -> Self {
1387 Self::default()
1388 }
1389 #[allow(dead_code)]
1390 pub fn visit(&mut self) {
1391 self.nodes_visited += 1;
1392 }
1393 #[allow(dead_code)]
1394 pub fn modify(&mut self) {
1395 self.nodes_modified += 1;
1396 self.changed = true;
1397 }
1398 #[allow(dead_code)]
1399 pub fn iterate(&mut self) {
1400 self.iterations += 1;
1401 }
1402 #[allow(dead_code)]
1403 pub fn error(&mut self) {
1404 self.errors += 1;
1405 }
1406 #[allow(dead_code)]
1407 pub fn efficiency(&self) -> f64 {
1408 if self.nodes_visited == 0 {
1409 0.0
1410 } else {
1411 self.nodes_modified as f64 / self.nodes_visited as f64
1412 }
1413 }
1414 #[allow(dead_code)]
1415 pub fn merge(&mut self, o: &SolExtPassStats) {
1416 self.iterations += o.iterations;
1417 self.changed |= o.changed;
1418 self.nodes_visited += o.nodes_visited;
1419 self.nodes_modified += o.nodes_modified;
1420 self.time_ms += o.time_ms;
1421 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1422 self.errors += o.errors;
1423 }
1424}
1425#[derive(Debug, Clone)]
1427pub struct SolidityModifier {
1428 pub name: String,
1429 pub params: Vec<SolidityParam>,
1430 pub body: Vec<SolidityStmt>,
1431 pub doc: Option<String>,
1432}
1433#[allow(dead_code)]
1435#[derive(Debug, Default)]
1436pub struct SolExtPassRegistry {
1437 pub(super) configs: Vec<SolExtPassConfig>,
1438 pub(super) stats: Vec<SolExtPassStats>,
1439}
1440impl SolExtPassRegistry {
1441 #[allow(dead_code)]
1442 pub fn new() -> Self {
1443 Self::default()
1444 }
1445 #[allow(dead_code)]
1446 pub fn register(&mut self, c: SolExtPassConfig) {
1447 self.stats.push(SolExtPassStats::new());
1448 self.configs.push(c);
1449 }
1450 #[allow(dead_code)]
1451 pub fn len(&self) -> usize {
1452 self.configs.len()
1453 }
1454 #[allow(dead_code)]
1455 pub fn is_empty(&self) -> bool {
1456 self.configs.is_empty()
1457 }
1458 #[allow(dead_code)]
1459 pub fn get(&self, i: usize) -> Option<&SolExtPassConfig> {
1460 self.configs.get(i)
1461 }
1462 #[allow(dead_code)]
1463 pub fn get_stats(&self, i: usize) -> Option<&SolExtPassStats> {
1464 self.stats.get(i)
1465 }
1466 #[allow(dead_code)]
1467 pub fn enabled_passes(&self) -> Vec<&SolExtPassConfig> {
1468 self.configs.iter().filter(|c| c.enabled).collect()
1469 }
1470 #[allow(dead_code)]
1471 pub fn passes_in_phase(&self, ph: &SolExtPassPhase) -> Vec<&SolExtPassConfig> {
1472 self.configs
1473 .iter()
1474 .filter(|c| c.enabled && &c.phase == ph)
1475 .collect()
1476 }
1477 #[allow(dead_code)]
1478 pub fn total_nodes_visited(&self) -> usize {
1479 self.stats.iter().map(|s| s.nodes_visited).sum()
1480 }
1481 #[allow(dead_code)]
1482 pub fn any_changed(&self) -> bool {
1483 self.stats.iter().any(|s| s.changed)
1484 }
1485}
1486#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1488pub enum ContractKind {
1489 Contract,
1490 Abstract,
1491 Interface,
1492 Library,
1493}
1494#[derive(Debug, Clone)]
1496pub struct SolidityFunction {
1497 pub name: String,
1499 pub params: Vec<SolidityParam>,
1501 pub returns: Vec<SolidityParam>,
1503 pub visibility: Visibility,
1505 pub mutability: StateMutability,
1507 pub is_virtual: bool,
1509 pub is_override: bool,
1511 pub modifiers: Vec<(String, Vec<SolidityExpr>)>,
1513 pub body: Vec<SolidityStmt>,
1515 pub doc: Option<String>,
1517}
1518impl SolidityFunction {
1519 pub fn new(name: impl Into<String>) -> Self {
1520 Self {
1521 name: name.into(),
1522 params: Vec::new(),
1523 returns: Vec::new(),
1524 visibility: Visibility::External,
1525 mutability: StateMutability::NonPayable,
1526 is_virtual: false,
1527 is_override: false,
1528 modifiers: Vec::new(),
1529 body: Vec::new(),
1530 doc: None,
1531 }
1532 }
1533 pub fn abi_signature(&self) -> String {
1535 let params: Vec<String> = self.params.iter().map(|p| p.ty.abi_canonical()).collect();
1536 format!("{}({})", self.name, params.join(","))
1537 }
1538 pub fn selector(&self) -> [u8; 4] {
1540 let sig = self.abi_signature();
1541 let mut h: u32 = 5381;
1542 for b in sig.bytes() {
1543 h = h.wrapping_shl(5).wrapping_add(h).wrapping_add(b as u32);
1544 }
1545 h.to_be_bytes()
1546 }
1547}
1548#[derive(Debug, Clone)]
1550pub enum SolidityStmt {
1551 VarDecl {
1553 ty: SolidityType,
1554 location: Option<String>,
1555 name: String,
1556 init: Option<SolidityExpr>,
1557 },
1558 Assign(SolidityExpr, SolidityExpr),
1560 CompoundAssign(String, SolidityExpr, SolidityExpr),
1562 ExprStmt(SolidityExpr),
1564 Return(Option<SolidityExpr>),
1566 If(SolidityExpr, Vec<SolidityStmt>, Vec<SolidityStmt>),
1568 While(SolidityExpr, Vec<SolidityStmt>),
1570 For(
1572 Option<Box<SolidityStmt>>,
1573 Option<SolidityExpr>,
1574 Option<Box<SolidityStmt>>,
1575 Vec<SolidityStmt>,
1576 ),
1577 DoWhile(Vec<SolidityStmt>, SolidityExpr),
1579 Emit(String, Vec<SolidityExpr>),
1581 Revert(String, Vec<SolidityExpr>),
1583 Require(SolidityExpr, Option<String>),
1585 Assert(SolidityExpr),
1587 Break,
1589 Continue,
1591 Unchecked(Vec<SolidityStmt>),
1593 Assembly(String),
1595 MultiAssign(Vec<SolidityExpr>, SolidityExpr),
1597 Block(Vec<SolidityStmt>),
1599}
1600#[allow(dead_code)]
1601#[derive(Debug, Clone)]
1602pub struct SolWorklist {
1603 pub(super) items: std::collections::VecDeque<u32>,
1604 pub(super) in_worklist: std::collections::HashSet<u32>,
1605}
1606impl SolWorklist {
1607 #[allow(dead_code)]
1608 pub fn new() -> Self {
1609 SolWorklist {
1610 items: std::collections::VecDeque::new(),
1611 in_worklist: std::collections::HashSet::new(),
1612 }
1613 }
1614 #[allow(dead_code)]
1615 pub fn push(&mut self, item: u32) -> bool {
1616 if self.in_worklist.insert(item) {
1617 self.items.push_back(item);
1618 true
1619 } else {
1620 false
1621 }
1622 }
1623 #[allow(dead_code)]
1624 pub fn pop(&mut self) -> Option<u32> {
1625 let item = self.items.pop_front()?;
1626 self.in_worklist.remove(&item);
1627 Some(item)
1628 }
1629 #[allow(dead_code)]
1630 pub fn is_empty(&self) -> bool {
1631 self.items.is_empty()
1632 }
1633 #[allow(dead_code)]
1634 pub fn len(&self) -> usize {
1635 self.items.len()
1636 }
1637 #[allow(dead_code)]
1638 pub fn contains(&self, item: u32) -> bool {
1639 self.in_worklist.contains(&item)
1640 }
1641}
1642#[derive(Debug, Clone)]
1644pub struct SolidityContract {
1645 pub name: String,
1646 pub kind: ContractKind,
1647 pub bases: Vec<String>,
1649 pub structs: Vec<SolidityStruct>,
1650 pub enums: Vec<SolidityEnum>,
1651 pub events: Vec<SolidityEvent>,
1652 pub errors: Vec<SolidityError>,
1653 pub state_vars: Vec<SolidityStateVar>,
1654 pub modifiers: Vec<SolidityModifier>,
1655 pub functions: Vec<SolidityFunction>,
1656 pub constructor: Option<SolidityFunction>,
1658 pub receive: Option<SolidityFunction>,
1660 pub fallback: Option<SolidityFunction>,
1662 pub doc: Option<String>,
1664}
1665impl SolidityContract {
1666 pub fn new(name: impl Into<String>, kind: ContractKind) -> Self {
1667 Self {
1668 name: name.into(),
1669 kind,
1670 bases: Vec::new(),
1671 structs: Vec::new(),
1672 enums: Vec::new(),
1673 events: Vec::new(),
1674 errors: Vec::new(),
1675 state_vars: Vec::new(),
1676 modifiers: Vec::new(),
1677 functions: Vec::new(),
1678 constructor: None,
1679 receive: None,
1680 fallback: None,
1681 doc: None,
1682 }
1683 }
1684}
1685#[derive(Debug, Clone)]
1687pub struct SolidityEvent {
1688 pub name: String,
1689 pub fields: Vec<(SolidityType, bool, String)>,
1691 pub anonymous: bool,
1692 pub doc: Option<String>,
1693}
1694#[allow(dead_code)]
1696#[derive(Debug, Clone, Default)]
1697pub struct SolExtLiveness {
1698 pub live_in: Vec<Vec<usize>>,
1699 pub live_out: Vec<Vec<usize>>,
1700 pub defs: Vec<Vec<usize>>,
1701 pub uses: Vec<Vec<usize>>,
1702}
1703impl SolExtLiveness {
1704 #[allow(dead_code)]
1705 pub fn new(n: usize) -> Self {
1706 Self {
1707 live_in: vec![Vec::new(); n],
1708 live_out: vec![Vec::new(); n],
1709 defs: vec![Vec::new(); n],
1710 uses: vec![Vec::new(); n],
1711 }
1712 }
1713 #[allow(dead_code)]
1714 pub fn live_in(&self, b: usize, v: usize) -> bool {
1715 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1716 }
1717 #[allow(dead_code)]
1718 pub fn live_out(&self, b: usize, v: usize) -> bool {
1719 self.live_out
1720 .get(b)
1721 .map(|s| s.contains(&v))
1722 .unwrap_or(false)
1723 }
1724 #[allow(dead_code)]
1725 pub fn add_def(&mut self, b: usize, v: usize) {
1726 if let Some(s) = self.defs.get_mut(b) {
1727 if !s.contains(&v) {
1728 s.push(v);
1729 }
1730 }
1731 }
1732 #[allow(dead_code)]
1733 pub fn add_use(&mut self, b: usize, v: usize) {
1734 if let Some(s) = self.uses.get_mut(b) {
1735 if !s.contains(&v) {
1736 s.push(v);
1737 }
1738 }
1739 }
1740 #[allow(dead_code)]
1741 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1742 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1743 }
1744 #[allow(dead_code)]
1745 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1746 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1747 }
1748}
1749#[allow(dead_code)]
1751#[derive(Debug, Clone, Default)]
1752pub struct SolExtConstFolder {
1753 pub(super) folds: usize,
1754 pub(super) failures: usize,
1755 pub(super) enabled: bool,
1756}
1757impl SolExtConstFolder {
1758 #[allow(dead_code)]
1759 pub fn new() -> Self {
1760 Self {
1761 folds: 0,
1762 failures: 0,
1763 enabled: true,
1764 }
1765 }
1766 #[allow(dead_code)]
1767 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1768 self.folds += 1;
1769 a.checked_add(b)
1770 }
1771 #[allow(dead_code)]
1772 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1773 self.folds += 1;
1774 a.checked_sub(b)
1775 }
1776 #[allow(dead_code)]
1777 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1778 self.folds += 1;
1779 a.checked_mul(b)
1780 }
1781 #[allow(dead_code)]
1782 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1783 if b == 0 {
1784 self.failures += 1;
1785 None
1786 } else {
1787 self.folds += 1;
1788 a.checked_div(b)
1789 }
1790 }
1791 #[allow(dead_code)]
1792 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1793 if b == 0 {
1794 self.failures += 1;
1795 None
1796 } else {
1797 self.folds += 1;
1798 a.checked_rem(b)
1799 }
1800 }
1801 #[allow(dead_code)]
1802 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
1803 self.folds += 1;
1804 a.checked_neg()
1805 }
1806 #[allow(dead_code)]
1807 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1808 if s >= 64 {
1809 self.failures += 1;
1810 None
1811 } else {
1812 self.folds += 1;
1813 a.checked_shl(s)
1814 }
1815 }
1816 #[allow(dead_code)]
1817 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1818 if s >= 64 {
1819 self.failures += 1;
1820 None
1821 } else {
1822 self.folds += 1;
1823 a.checked_shr(s)
1824 }
1825 }
1826 #[allow(dead_code)]
1827 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
1828 self.folds += 1;
1829 a & b
1830 }
1831 #[allow(dead_code)]
1832 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
1833 self.folds += 1;
1834 a | b
1835 }
1836 #[allow(dead_code)]
1837 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
1838 self.folds += 1;
1839 a ^ b
1840 }
1841 #[allow(dead_code)]
1842 pub fn not_i64(&mut self, a: i64) -> i64 {
1843 self.folds += 1;
1844 !a
1845 }
1846 #[allow(dead_code)]
1847 pub fn fold_count(&self) -> usize {
1848 self.folds
1849 }
1850 #[allow(dead_code)]
1851 pub fn failure_count(&self) -> usize {
1852 self.failures
1853 }
1854 #[allow(dead_code)]
1855 pub fn enable(&mut self) {
1856 self.enabled = true;
1857 }
1858 #[allow(dead_code)]
1859 pub fn disable(&mut self) {
1860 self.enabled = false;
1861 }
1862 #[allow(dead_code)]
1863 pub fn is_enabled(&self) -> bool {
1864 self.enabled
1865 }
1866}
1867#[allow(dead_code)]
1869#[derive(Debug, Clone)]
1870pub struct SolExtDepGraph {
1871 pub(super) n: usize,
1872 pub(super) adj: Vec<Vec<usize>>,
1873 pub(super) rev: Vec<Vec<usize>>,
1874 pub(super) edge_count: usize,
1875}
1876impl SolExtDepGraph {
1877 #[allow(dead_code)]
1878 pub fn new(n: usize) -> Self {
1879 Self {
1880 n,
1881 adj: vec![Vec::new(); n],
1882 rev: vec![Vec::new(); n],
1883 edge_count: 0,
1884 }
1885 }
1886 #[allow(dead_code)]
1887 pub fn add_edge(&mut self, from: usize, to: usize) {
1888 if from < self.n && to < self.n {
1889 if !self.adj[from].contains(&to) {
1890 self.adj[from].push(to);
1891 self.rev[to].push(from);
1892 self.edge_count += 1;
1893 }
1894 }
1895 }
1896 #[allow(dead_code)]
1897 pub fn succs(&self, n: usize) -> &[usize] {
1898 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1899 }
1900 #[allow(dead_code)]
1901 pub fn preds(&self, n: usize) -> &[usize] {
1902 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1903 }
1904 #[allow(dead_code)]
1905 pub fn topo_sort(&self) -> Option<Vec<usize>> {
1906 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
1907 let mut q: std::collections::VecDeque<usize> =
1908 (0..self.n).filter(|&i| deg[i] == 0).collect();
1909 let mut out = Vec::with_capacity(self.n);
1910 while let Some(u) = q.pop_front() {
1911 out.push(u);
1912 for &v in &self.adj[u] {
1913 deg[v] -= 1;
1914 if deg[v] == 0 {
1915 q.push_back(v);
1916 }
1917 }
1918 }
1919 if out.len() == self.n {
1920 Some(out)
1921 } else {
1922 None
1923 }
1924 }
1925 #[allow(dead_code)]
1926 pub fn has_cycle(&self) -> bool {
1927 self.topo_sort().is_none()
1928 }
1929 #[allow(dead_code)]
1930 pub fn reachable(&self, start: usize) -> Vec<usize> {
1931 let mut vis = vec![false; self.n];
1932 let mut stk = vec![start];
1933 let mut out = Vec::new();
1934 while let Some(u) = stk.pop() {
1935 if u < self.n && !vis[u] {
1936 vis[u] = true;
1937 out.push(u);
1938 for &v in &self.adj[u] {
1939 if !vis[v] {
1940 stk.push(v);
1941 }
1942 }
1943 }
1944 }
1945 out
1946 }
1947 #[allow(dead_code)]
1948 pub fn scc(&self) -> Vec<Vec<usize>> {
1949 let mut visited = vec![false; self.n];
1950 let mut order = Vec::new();
1951 for i in 0..self.n {
1952 if !visited[i] {
1953 let mut stk = vec![(i, 0usize)];
1954 while let Some((u, idx)) = stk.last_mut() {
1955 if !visited[*u] {
1956 visited[*u] = true;
1957 }
1958 if *idx < self.adj[*u].len() {
1959 let v = self.adj[*u][*idx];
1960 *idx += 1;
1961 if !visited[v] {
1962 stk.push((v, 0));
1963 }
1964 } else {
1965 order.push(*u);
1966 stk.pop();
1967 }
1968 }
1969 }
1970 }
1971 let mut comp = vec![usize::MAX; self.n];
1972 let mut components: Vec<Vec<usize>> = Vec::new();
1973 for &start in order.iter().rev() {
1974 if comp[start] == usize::MAX {
1975 let cid = components.len();
1976 let mut component = Vec::new();
1977 let mut stk = vec![start];
1978 while let Some(u) = stk.pop() {
1979 if comp[u] == usize::MAX {
1980 comp[u] = cid;
1981 component.push(u);
1982 for &v in &self.rev[u] {
1983 if comp[v] == usize::MAX {
1984 stk.push(v);
1985 }
1986 }
1987 }
1988 }
1989 components.push(component);
1990 }
1991 }
1992 components
1993 }
1994 #[allow(dead_code)]
1995 pub fn node_count(&self) -> usize {
1996 self.n
1997 }
1998 #[allow(dead_code)]
1999 pub fn edge_count(&self) -> usize {
2000 self.edge_count
2001 }
2002}
2003#[allow(dead_code)]
2004pub struct SolConstantFoldingHelper;
2005impl SolConstantFoldingHelper {
2006 #[allow(dead_code)]
2007 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
2008 a.checked_add(b)
2009 }
2010 #[allow(dead_code)]
2011 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
2012 a.checked_sub(b)
2013 }
2014 #[allow(dead_code)]
2015 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
2016 a.checked_mul(b)
2017 }
2018 #[allow(dead_code)]
2019 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
2020 if b == 0 {
2021 None
2022 } else {
2023 a.checked_div(b)
2024 }
2025 }
2026 #[allow(dead_code)]
2027 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
2028 a + b
2029 }
2030 #[allow(dead_code)]
2031 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
2032 a * b
2033 }
2034 #[allow(dead_code)]
2035 pub fn fold_neg_i64(a: i64) -> Option<i64> {
2036 a.checked_neg()
2037 }
2038 #[allow(dead_code)]
2039 pub fn fold_not_bool(a: bool) -> bool {
2040 !a
2041 }
2042 #[allow(dead_code)]
2043 pub fn fold_and_bool(a: bool, b: bool) -> bool {
2044 a && b
2045 }
2046 #[allow(dead_code)]
2047 pub fn fold_or_bool(a: bool, b: bool) -> bool {
2048 a || b
2049 }
2050 #[allow(dead_code)]
2051 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
2052 a.checked_shl(b)
2053 }
2054 #[allow(dead_code)]
2055 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
2056 a.checked_shr(b)
2057 }
2058 #[allow(dead_code)]
2059 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
2060 if b == 0 {
2061 None
2062 } else {
2063 Some(a % b)
2064 }
2065 }
2066 #[allow(dead_code)]
2067 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
2068 a & b
2069 }
2070 #[allow(dead_code)]
2071 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
2072 a | b
2073 }
2074 #[allow(dead_code)]
2075 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
2076 a ^ b
2077 }
2078 #[allow(dead_code)]
2079 pub fn fold_bitnot_i64(a: i64) -> i64 {
2080 !a
2081 }
2082}