Skip to main content

oxilean_codegen/solidity_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::HashMap;
6
7use super::functions::SOLIDITY_RUNTIME;
8
9use std::collections::{HashSet, VecDeque};
10
11/// State mutability of a function.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum StateMutability {
14    /// May read and modify state.
15    NonPayable,
16    /// May receive Ether.
17    Payable,
18    /// Reads state but does not modify it.
19    View,
20    /// Does not read or modify state.
21    Pure,
22}
23/// Dominator tree for SolExt.
24#[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/// Solidity expression AST node.
95#[derive(Debug, Clone, PartialEq)]
96pub enum SolidityExpr {
97    /// Integer literal: `42`
98    IntLit(i128),
99    /// Boolean literal: `true` / `false`
100    BoolLit(bool),
101    /// String literal: `"hello"`
102    StrLit(String),
103    /// Address literal: `0x1234...`
104    AddressLit(String),
105    /// Hex literal: `0xdeadbeef`
106    HexLit(String),
107    /// Variable reference: `myVar`
108    Var(String),
109    /// `this`
110    This,
111    /// `msg.sender`
112    MsgSender,
113    /// `msg.value`
114    MsgValue,
115    /// `msg.data`
116    MsgData,
117    /// `block.timestamp`
118    BlockTimestamp,
119    /// `block.number`
120    BlockNumber,
121    /// `block.basefee`
122    BlockBasefee,
123    /// `tx.origin`
124    TxOrigin,
125    /// `gasleft()`
126    GasLeft,
127    /// Field access: `expr.field`
128    FieldAccess(Box<SolidityExpr>, String),
129    /// Index access: `expr[index]`
130    Index(Box<SolidityExpr>, Box<SolidityExpr>),
131    /// Function call: `f(args...)`
132    Call(Box<SolidityExpr>, Vec<SolidityExpr>),
133    /// Named argument call: `f({name: val, ...})`
134    NamedCall(Box<SolidityExpr>, Vec<(String, SolidityExpr)>),
135    /// Type cast: `uint256(expr)`
136    Cast(SolidityType, Box<SolidityExpr>),
137    /// `abi.encode(args...)`
138    AbiEncode(Vec<SolidityExpr>),
139    /// `abi.encodePacked(args...)`
140    AbiEncodePacked(Vec<SolidityExpr>),
141    /// `abi.encodeWithSelector(selector, args...)`
142    AbiEncodeWithSelector(Box<SolidityExpr>, Vec<SolidityExpr>),
143    /// `keccak256(data)`
144    Keccak256(Box<SolidityExpr>),
145    /// `sha256(data)`
146    Sha256(Box<SolidityExpr>),
147    /// `ecrecover(hash, v, r, s)`
148    Ecrecover(
149        Box<SolidityExpr>,
150        Box<SolidityExpr>,
151        Box<SolidityExpr>,
152        Box<SolidityExpr>,
153    ),
154    /// Binary operation: `a + b`
155    BinOp(String, Box<SolidityExpr>, Box<SolidityExpr>),
156    /// Unary operation: `!a`, `-a`, `~a`
157    UnaryOp(String, Box<SolidityExpr>),
158    /// Ternary: `cond ? then_ : else_`
159    Ternary(Box<SolidityExpr>, Box<SolidityExpr>, Box<SolidityExpr>),
160    /// `new T(args...)`
161    New(SolidityType, Vec<SolidityExpr>),
162    /// `delete expr`
163    Delete(Box<SolidityExpr>),
164    /// Array literal: `[a, b, c]`
165    ArrayLit(Vec<SolidityExpr>),
166    /// Tuple literal: `(a, b, c)`
167    TupleLit(Vec<SolidityExpr>),
168    /// `type(T).max` / `type(T).min`
169    TypeMax(SolidityType),
170    TypeMin(SolidityType),
171    /// `payable(addr)`
172    Payable(Box<SolidityExpr>),
173}
174/// A struct definition.
175#[derive(Debug, Clone)]
176pub struct SolidityStruct {
177    pub name: String,
178    pub fields: Vec<(SolidityType, String)>,
179    pub doc: Option<String>,
180}
181/// A function parameter or return value.
182#[derive(Debug, Clone)]
183pub struct SolidityParam {
184    /// Parameter type.
185    pub ty: SolidityType,
186    /// Optional data location (`memory`, `calldata`, `storage`).
187    pub location: Option<String>,
188    /// Parameter name (may be empty for returns).
189    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/// Compilation context for a single Solidity source unit.
220#[derive(Debug, Clone)]
221pub struct CompilationCtx {
222    /// Pragma directives.
223    pub pragmas: Vec<String>,
224    /// Import statements.
225    pub imports: Vec<String>,
226    /// Whether to include the runtime library.
227    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/// Pass execution phase for SolExt.
272#[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/// Worklist for SolExt.
318#[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/// An enum definition.
378#[derive(Debug, Clone)]
379pub struct SolidityEnum {
380    pub name: String,
381    pub variants: Vec<String>,
382    pub doc: Option<String>,
383}
384/// A state variable in a contract.
385#[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/// The main Solidity code generation backend.
396#[derive(Debug, Default)]
397pub struct SolidityBackend {
398    /// Emitted contracts (in order).
399    pub contracts: Vec<SolidityContract>,
400    /// Compilation context.
401    pub ctx: CompilationCtx,
402    /// Type alias table: `alias → canonical SolidityType`.
403    pub type_aliases: HashMap<String, SolidityType>,
404    /// Source buffer accumulated during emission.
405    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    /// Compile a single LCNF-style declaration (simplified: name → state var).
425    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    /// Emit the full Solidity source for all registered contracts.
437    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/// A custom Solidity error definition (Solidity 0.8+).
798#[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/// Analysis cache for SolExt.
858#[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/// Solidity ABI type representation.
994#[derive(Debug, Clone, PartialEq, Eq, Hash)]
995pub enum SolidityType {
996    /// `uint256`
997    Uint256,
998    /// `uint128`
999    Uint128,
1000    /// `uint64`
1001    Uint64,
1002    /// `uint32`
1003    Uint32,
1004    /// `uint8`
1005    Uint8,
1006    /// `int256`
1007    Int256,
1008    /// `int128`
1009    Int128,
1010    /// `int64`
1011    Int64,
1012    /// `int32`
1013    Int32,
1014    /// `int8`
1015    Int8,
1016    /// `address`
1017    Address,
1018    /// `address payable`
1019    AddressPayable,
1020    /// `bool`
1021    Bool,
1022    /// `bytes`
1023    Bytes,
1024    /// `bytes32`
1025    Bytes32,
1026    /// `bytes16`
1027    Bytes16,
1028    /// `bytes4`
1029    Bytes4,
1030    /// `string`
1031    StringTy,
1032    /// `mapping(K => V)`
1033    Mapping(Box<SolidityType>, Box<SolidityType>),
1034    /// `T[]` — dynamic array
1035    DynArray(Box<SolidityType>),
1036    /// `T[N]` — fixed-size array
1037    FixedArray(Box<SolidityType>, usize),
1038    /// A named struct or enum type
1039    Named(String),
1040    /// `tuple(T0, T1, ...)` — used for ABI encoding
1041    Tuple(Vec<SolidityType>),
1042}
1043impl SolidityType {
1044    /// Returns true if this type is a reference type (stored in memory/storage).
1045    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    /// Returns the default data location for function parameters of this type.
1058    pub fn default_param_location(&self) -> &'static str {
1059        if self.is_reference_type() {
1060            "memory"
1061        } else {
1062            ""
1063        }
1064    }
1065    /// Returns the ABI-canonical type string (for selector computation).
1066    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/// Visibility of a state variable or function.
1221#[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/// Configuration for SolExt passes.
1320#[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/// Statistics for SolExt passes.
1373#[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/// A Solidity modifier definition.
1426#[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/// Pass registry for SolExt.
1434#[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/// Contract kind.
1487#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1488pub enum ContractKind {
1489    Contract,
1490    Abstract,
1491    Interface,
1492    Library,
1493}
1494/// A Solidity function (or constructor / fallback / receive).
1495#[derive(Debug, Clone)]
1496pub struct SolidityFunction {
1497    /// Function name (empty for constructor/fallback/receive).
1498    pub name: String,
1499    /// Input parameters.
1500    pub params: Vec<SolidityParam>,
1501    /// Return parameters.
1502    pub returns: Vec<SolidityParam>,
1503    /// Visibility.
1504    pub visibility: Visibility,
1505    /// State mutability.
1506    pub mutability: StateMutability,
1507    /// Whether this is `virtual`.
1508    pub is_virtual: bool,
1509    /// Whether this overrides a base function.
1510    pub is_override: bool,
1511    /// List of modifier invocations: `(name, args)`.
1512    pub modifiers: Vec<(String, Vec<SolidityExpr>)>,
1513    /// Function body statements (empty = abstract/interface).
1514    pub body: Vec<SolidityStmt>,
1515    /// NatSpec dev comment.
1516    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    /// ABI signature for selector computation: `name(T0,T1,...)`
1534    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    /// Simple 4-byte selector (djb2-based placeholder, not real keccak).
1539    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/// Solidity statement AST node.
1549#[derive(Debug, Clone)]
1550pub enum SolidityStmt {
1551    /// Variable declaration: `T loc name = expr;`
1552    VarDecl {
1553        ty: SolidityType,
1554        location: Option<String>,
1555        name: String,
1556        init: Option<SolidityExpr>,
1557    },
1558    /// Assignment: `lhs = rhs;`
1559    Assign(SolidityExpr, SolidityExpr),
1560    /// Compound assignment: `lhs += rhs;`
1561    CompoundAssign(String, SolidityExpr, SolidityExpr),
1562    /// Expression statement: `f();`
1563    ExprStmt(SolidityExpr),
1564    /// `return expr;`
1565    Return(Option<SolidityExpr>),
1566    /// `if (cond) { then } else { else_ }`
1567    If(SolidityExpr, Vec<SolidityStmt>, Vec<SolidityStmt>),
1568    /// `while (cond) { body }`
1569    While(SolidityExpr, Vec<SolidityStmt>),
1570    /// `for (init; cond; update) { body }`
1571    For(
1572        Option<Box<SolidityStmt>>,
1573        Option<SolidityExpr>,
1574        Option<Box<SolidityStmt>>,
1575        Vec<SolidityStmt>,
1576    ),
1577    /// `do { body } while (cond);`
1578    DoWhile(Vec<SolidityStmt>, SolidityExpr),
1579    /// `emit EventName(args...);`
1580    Emit(String, Vec<SolidityExpr>),
1581    /// `revert ErrorName(args...);`
1582    Revert(String, Vec<SolidityExpr>),
1583    /// `require(cond, msg);`
1584    Require(SolidityExpr, Option<String>),
1585    /// `assert(cond);`
1586    Assert(SolidityExpr),
1587    /// `break;`
1588    Break,
1589    /// `continue;`
1590    Continue,
1591    /// `unchecked { stmts }`
1592    Unchecked(Vec<SolidityStmt>),
1593    /// `assembly { body }`
1594    Assembly(String),
1595    /// Multi-return: `(a, b) = f();`
1596    MultiAssign(Vec<SolidityExpr>, SolidityExpr),
1597    /// Block of statements `{ ... }`
1598    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/// A complete Solidity contract, interface, abstract contract, or library.
1643#[derive(Debug, Clone)]
1644pub struct SolidityContract {
1645    pub name: String,
1646    pub kind: ContractKind,
1647    /// Inheritance list.
1648    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    /// Constructor (if present).
1657    pub constructor: Option<SolidityFunction>,
1658    /// Receive function (if present).
1659    pub receive: Option<SolidityFunction>,
1660    /// Fallback function (if present).
1661    pub fallback: Option<SolidityFunction>,
1662    /// NatSpec title/dev comment.
1663    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/// A Solidity event definition.
1686#[derive(Debug, Clone)]
1687pub struct SolidityEvent {
1688    pub name: String,
1689    /// `(ty, indexed, name)`
1690    pub fields: Vec<(SolidityType, bool, String)>,
1691    pub anonymous: bool,
1692    pub doc: Option<String>,
1693}
1694/// Liveness analysis for SolExt.
1695#[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/// Constant folding helper for SolExt.
1750#[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/// Dependency graph for SolExt.
1868#[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}