Skip to main content

oxilean_codegen/evm_backend/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::HashMap;
6
7use super::types::{
8    EVMAnalysisCache, EVMConstantFoldingHelper, EVMDepGraph, EVMDominatorTree, EVMLivenessInfo,
9    EVMPassConfig, EVMPassPhase, EVMPassRegistry, EVMPassStats, EVMWorklist, EvmBackend,
10    EvmBasicBlock, EvmContract, EvmInstruction, EvmOpcode, EvmOpcodeCategory, EvmOpcodeDesc,
11    StorageLayout,
12};
13
14#[cfg(test)]
15mod tests {
16    use super::*;
17    #[test]
18    pub(super) fn test_opcode_bytes() {
19        assert_eq!(EvmOpcode::Stop.byte(), 0x00);
20        assert_eq!(EvmOpcode::Add.byte(), 0x01);
21        assert_eq!(EvmOpcode::Push1.byte(), 0x60);
22        assert_eq!(EvmOpcode::Push32.byte(), 0x7f);
23        assert_eq!(EvmOpcode::Dup1.byte(), 0x80);
24        assert_eq!(EvmOpcode::Swap1.byte(), 0x90);
25        assert_eq!(EvmOpcode::Return.byte(), 0xf3);
26        assert_eq!(EvmOpcode::Revert.byte(), 0xfd);
27        assert_eq!(EvmOpcode::Invalid.byte(), 0xfe);
28    }
29    #[test]
30    pub(super) fn test_opcode_mnemonic() {
31        assert_eq!(EvmOpcode::Add.mnemonic(), "ADD");
32        assert_eq!(EvmOpcode::Mstore.mnemonic(), "MSTORE");
33        assert_eq!(EvmOpcode::Calldataload.mnemonic(), "CALLDATALOAD");
34        assert_eq!(EvmOpcode::Jumpdest.mnemonic(), "JUMPDEST");
35    }
36    #[test]
37    pub(super) fn test_instruction_encode() {
38        let stop = EvmInstruction::new(EvmOpcode::Stop);
39        assert_eq!(stop.encode(), vec![0x00]);
40        assert_eq!(stop.byte_len(), 1);
41        let push1 = EvmInstruction::push1(0x42);
42        assert_eq!(push1.encode(), vec![0x60, 0x42]);
43        assert_eq!(push1.byte_len(), 2);
44        let push4 = EvmInstruction::push4(0xdeadbeef);
45        assert_eq!(push4.encode(), vec![0x63, 0xde, 0xad, 0xbe, 0xef]);
46        assert_eq!(push4.byte_len(), 5);
47    }
48    #[test]
49    pub(super) fn test_push_auto_size() {
50        let p =
51            EvmInstruction::push(vec![0x01, 0x02, 0x03]).expect("p instruction should be valid");
52        assert_eq!(p.opcode, EvmOpcode::Push3);
53        assert_eq!(p.encode(), vec![0x62, 0x01, 0x02, 0x03]);
54        assert!(EvmInstruction::push(vec![]).is_none());
55        assert!(EvmInstruction::push(vec![0u8; 33]).is_none());
56    }
57    #[test]
58    pub(super) fn test_basic_block_encode() {
59        let mut block = EvmBasicBlock::new_jump_target("entry");
60        block.push_op(EvmOpcode::Caller);
61        block.push_op(EvmOpcode::Stop);
62        let bytes = block.encode();
63        assert_eq!(bytes, vec![0x5b, 0x33, 0x00]);
64        assert_eq!(block.byte_len(), 3);
65    }
66    #[test]
67    pub(super) fn test_storage_layout() {
68        let mut layout = StorageLayout::new();
69        assert!(layout.is_empty());
70        let slot_a = layout.allocate("balance");
71        let slot_b = layout.allocate("totalSupply");
72        assert_eq!(slot_a, 0);
73        assert_eq!(slot_b, 1);
74        assert_eq!(layout.slot_of("balance"), Some(0));
75        assert_eq!(layout.slot_of("totalSupply"), Some(1));
76        assert_eq!(layout.slot_of("unknown"), None);
77        assert_eq!(layout.len(), 2);
78        assert!(!layout.is_empty());
79    }
80    #[test]
81    pub(super) fn test_compute_selector() {
82        let sel = EvmBackend::compute_selector("transfer(address,uint256)");
83        let sel2 = EvmBackend::compute_selector("transfer(address,uint256)");
84        assert_eq!(sel, sel2);
85        let sel3 = EvmBackend::compute_selector("balanceOf(address)");
86        assert_ne!(sel, sel3);
87    }
88    #[test]
89    pub(super) fn test_compute_selector_canonical_ethereum_values() {
90        // These are the canonical Ethereum ABI selectors per the specification.
91        // Verify: first 4 bytes of keccak256(canonical_signature).
92        assert_eq!(
93            EvmBackend::compute_selector("transfer(address,uint256)"),
94            [0xa9, 0x05, 0x9c, 0xbb],
95            "transfer(address,uint256) selector mismatch"
96        );
97        assert_eq!(
98            EvmBackend::compute_selector("balanceOf(address)"),
99            [0x70, 0xa0, 0x82, 0x31],
100            "balanceOf(address) selector mismatch"
101        );
102        assert_eq!(
103            EvmBackend::compute_selector("approve(address,uint256)"),
104            [0x09, 0x5e, 0xa7, 0xb3],
105            "approve(address,uint256) selector mismatch"
106        );
107        assert_eq!(
108            EvmBackend::compute_selector("allowance(address,address)"),
109            [0xdd, 0x62, 0xed, 0x3e],
110            "allowance(address,address) selector mismatch"
111        );
112        assert_eq!(
113            EvmBackend::compute_selector("totalSupply()"),
114            [0x18, 0x16, 0x0d, 0xdd],
115            "totalSupply() selector mismatch"
116        );
117        // Additional well-known selector: ERC-721 ownerOf(uint256) → 0x6352211e
118        assert_eq!(
119            EvmBackend::compute_selector("ownerOf(uint256)"),
120            [0x63, 0x52, 0x21, 0x1e],
121            "ownerOf(uint256) selector mismatch"
122        );
123    }
124    #[test]
125    pub(super) fn test_evm_keccak256_known_value() {
126        // keccak256("") = c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
127        let hash = super::evm_keccak256(b"");
128        assert_eq!(hash[0], 0xc5);
129        assert_eq!(hash[1], 0xd2);
130        assert_eq!(hash[2], 0x46);
131        assert_eq!(hash[3], 0x01);
132    }
133    #[test]
134    pub(super) fn test_build_arithmetic_function() {
135        let sel = EvmBackend::compute_selector("add(uint256,uint256)");
136        let func = EvmBackend::build_arithmetic_function(
137            "add",
138            "add(uint256,uint256)",
139            sel,
140            EvmOpcode::Add,
141        );
142        assert_eq!(func.name, "add");
143        assert_eq!(func.selector, sel);
144        assert_eq!(func.blocks.len(), 1);
145        assert!(func.blocks[0].is_jump_target);
146        let bytes = func.encode();
147        assert!(!bytes.is_empty());
148        assert_eq!(
149            *bytes.last().expect("collection should not be empty"),
150            EvmOpcode::Return.byte()
151        );
152    }
153    #[test]
154    pub(super) fn test_emit_hex_and_assembly() {
155        let backend = EvmBackend::new();
156        let mut contract = EvmContract::new("SimpleToken");
157        contract.set_metadata("version", "0.8.0");
158        contract.allocate_storage("_balance");
159        contract.allocate_storage("_totalSupply");
160        let sel = EvmBackend::compute_selector("add(uint256,uint256)");
161        let func = EvmBackend::build_arithmetic_function(
162            "add",
163            "add(uint256,uint256)",
164            sel,
165            EvmOpcode::Add,
166        );
167        contract.add_function(func);
168        let hex = backend.emit_hex(&contract);
169        assert!(!hex.is_empty());
170        assert!(hex.chars().all(|c| c.is_ascii_hexdigit()));
171        let hex_prefixed = backend.emit_hex_prefixed(&contract);
172        assert!(hex_prefixed.starts_with("0x"));
173        let asm = backend.emit_assembly(&contract);
174        assert!(asm.contains("SimpleToken"));
175        assert!(asm.contains("CALLDATALOAD"));
176        assert!(asm.contains("_balance"));
177        assert!(asm.contains("_totalSupply"));
178        assert!(asm.contains("add"));
179    }
180}
181/// EVM standard opcode table
182#[allow(dead_code)]
183pub fn evm_opcode_table() -> Vec<EvmOpcodeDesc> {
184    vec![
185        EvmOpcodeDesc {
186            name: "STOP".into(),
187            opcode: 0x00,
188            stack_in: 0,
189            stack_out: 0,
190            gas: 0,
191            category: EvmOpcodeCategory::Stop,
192            description: "Halts execution".into(),
193        },
194        EvmOpcodeDesc {
195            name: "ADD".into(),
196            opcode: 0x01,
197            stack_in: 2,
198            stack_out: 1,
199            gas: 3,
200            category: EvmOpcodeCategory::Arithmetic,
201            description: "Addition".into(),
202        },
203        EvmOpcodeDesc {
204            name: "MUL".into(),
205            opcode: 0x02,
206            stack_in: 2,
207            stack_out: 1,
208            gas: 5,
209            category: EvmOpcodeCategory::Arithmetic,
210            description: "Multiplication".into(),
211        },
212        EvmOpcodeDesc {
213            name: "SUB".into(),
214            opcode: 0x03,
215            stack_in: 2,
216            stack_out: 1,
217            gas: 3,
218            category: EvmOpcodeCategory::Arithmetic,
219            description: "Subtraction".into(),
220        },
221        EvmOpcodeDesc {
222            name: "DIV".into(),
223            opcode: 0x04,
224            stack_in: 2,
225            stack_out: 1,
226            gas: 5,
227            category: EvmOpcodeCategory::Arithmetic,
228            description: "Integer division".into(),
229        },
230        EvmOpcodeDesc {
231            name: "MOD".into(),
232            opcode: 0x06,
233            stack_in: 2,
234            stack_out: 1,
235            gas: 5,
236            category: EvmOpcodeCategory::Arithmetic,
237            description: "Modulo".into(),
238        },
239        EvmOpcodeDesc {
240            name: "EXP".into(),
241            opcode: 0x0a,
242            stack_in: 2,
243            stack_out: 1,
244            gas: 10,
245            category: EvmOpcodeCategory::Arithmetic,
246            description: "Exponentiation".into(),
247        },
248        EvmOpcodeDesc {
249            name: "LT".into(),
250            opcode: 0x10,
251            stack_in: 2,
252            stack_out: 1,
253            gas: 3,
254            category: EvmOpcodeCategory::Comparison,
255            description: "Less than".into(),
256        },
257        EvmOpcodeDesc {
258            name: "GT".into(),
259            opcode: 0x11,
260            stack_in: 2,
261            stack_out: 1,
262            gas: 3,
263            category: EvmOpcodeCategory::Comparison,
264            description: "Greater than".into(),
265        },
266        EvmOpcodeDesc {
267            name: "EQ".into(),
268            opcode: 0x14,
269            stack_in: 2,
270            stack_out: 1,
271            gas: 3,
272            category: EvmOpcodeCategory::Comparison,
273            description: "Equality".into(),
274        },
275        EvmOpcodeDesc {
276            name: "ISZERO".into(),
277            opcode: 0x15,
278            stack_in: 1,
279            stack_out: 1,
280            gas: 3,
281            category: EvmOpcodeCategory::Comparison,
282            description: "Is zero".into(),
283        },
284        EvmOpcodeDesc {
285            name: "AND".into(),
286            opcode: 0x16,
287            stack_in: 2,
288            stack_out: 1,
289            gas: 3,
290            category: EvmOpcodeCategory::Bitwise,
291            description: "Bitwise AND".into(),
292        },
293        EvmOpcodeDesc {
294            name: "OR".into(),
295            opcode: 0x17,
296            stack_in: 2,
297            stack_out: 1,
298            gas: 3,
299            category: EvmOpcodeCategory::Bitwise,
300            description: "Bitwise OR".into(),
301        },
302        EvmOpcodeDesc {
303            name: "XOR".into(),
304            opcode: 0x18,
305            stack_in: 2,
306            stack_out: 1,
307            gas: 3,
308            category: EvmOpcodeCategory::Bitwise,
309            description: "Bitwise XOR".into(),
310        },
311        EvmOpcodeDesc {
312            name: "NOT".into(),
313            opcode: 0x19,
314            stack_in: 1,
315            stack_out: 1,
316            gas: 3,
317            category: EvmOpcodeCategory::Bitwise,
318            description: "Bitwise NOT".into(),
319        },
320        EvmOpcodeDesc {
321            name: "SHA3".into(),
322            opcode: 0x20,
323            stack_in: 2,
324            stack_out: 1,
325            gas: 30,
326            category: EvmOpcodeCategory::Sha3,
327            description: "Keccak-256".into(),
328        },
329        EvmOpcodeDesc {
330            name: "SLOAD".into(),
331            opcode: 0x54,
332            stack_in: 1,
333            stack_out: 1,
334            gas: 2100,
335            category: EvmOpcodeCategory::Storage,
336            description: "Load storage".into(),
337        },
338        EvmOpcodeDesc {
339            name: "SSTORE".into(),
340            opcode: 0x55,
341            stack_in: 2,
342            stack_out: 0,
343            gas: 20000,
344            category: EvmOpcodeCategory::Storage,
345            description: "Store to storage".into(),
346        },
347        EvmOpcodeDesc {
348            name: "JUMP".into(),
349            opcode: 0x56,
350            stack_in: 1,
351            stack_out: 0,
352            gas: 8,
353            category: EvmOpcodeCategory::Control,
354            description: "Unconditional jump".into(),
355        },
356        EvmOpcodeDesc {
357            name: "JUMPI".into(),
358            opcode: 0x57,
359            stack_in: 2,
360            stack_out: 0,
361            gas: 10,
362            category: EvmOpcodeCategory::Control,
363            description: "Conditional jump".into(),
364        },
365        EvmOpcodeDesc {
366            name: "RETURN".into(),
367            opcode: 0xf3,
368            stack_in: 2,
369            stack_out: 0,
370            gas: 0,
371            category: EvmOpcodeCategory::System,
372            description: "Return from call".into(),
373        },
374        EvmOpcodeDesc {
375            name: "REVERT".into(),
376            opcode: 0xfd,
377            stack_in: 2,
378            stack_out: 0,
379            gas: 0,
380            category: EvmOpcodeCategory::System,
381            description: "Revert".into(),
382        },
383        EvmOpcodeDesc {
384            name: "CALL".into(),
385            opcode: 0xf1,
386            stack_in: 7,
387            stack_out: 1,
388            gas: 100,
389            category: EvmOpcodeCategory::System,
390            description: "Message call".into(),
391        },
392        EvmOpcodeDesc {
393            name: "DELEGATECALL".into(),
394            opcode: 0xf4,
395            stack_in: 6,
396            stack_out: 1,
397            gas: 100,
398            category: EvmOpcodeCategory::System,
399            description: "Delegatecall".into(),
400        },
401        EvmOpcodeDesc {
402            name: "STATICCALL".into(),
403            opcode: 0xfa,
404            stack_in: 6,
405            stack_out: 1,
406            gas: 100,
407            category: EvmOpcodeCategory::System,
408            description: "Staticcall".into(),
409        },
410        EvmOpcodeDesc {
411            name: "CREATE".into(),
412            opcode: 0xf0,
413            stack_in: 3,
414            stack_out: 1,
415            gas: 32000,
416            category: EvmOpcodeCategory::System,
417            description: "Create contract".into(),
418        },
419        EvmOpcodeDesc {
420            name: "CREATE2".into(),
421            opcode: 0xf5,
422            stack_in: 4,
423            stack_out: 1,
424            gas: 32000,
425            category: EvmOpcodeCategory::System,
426            description: "Create2 contract".into(),
427        },
428    ]
429}
430/// EVM version string
431#[allow(dead_code)]
432pub const EVM_PASS_VERSION: &str = "1.0.0";
433/// EVM address size
434#[allow(dead_code)]
435pub const EVM_ADDRESS_SIZE_BYTES: usize = 20;
436/// EVM word size
437#[allow(dead_code)]
438pub const EVM_WORD_SIZE_BYTES: usize = 32;
439/// EVM stack max depth
440#[allow(dead_code)]
441pub const EVM_STACK_MAX_DEPTH: usize = 1024;
442/// EVM max code size (EIP-170)
443#[allow(dead_code)]
444pub const EVM_MAX_CODE_SIZE: usize = 24576;
445/// EVM max init code size (EIP-3860)
446#[allow(dead_code)]
447pub const EVM_MAX_INIT_CODE_SIZE: usize = 49152;
448/// EVM gas estimation
449#[allow(dead_code)]
450pub fn evm_estimate_gas(bytecode_len: usize, storage_ops: usize, call_ops: usize) -> u64 {
451    let base = 21_000u64;
452    let code_gas = (bytecode_len / 32) as u64 * 200;
453    let storage_gas = storage_ops as u64 * 20_000;
454    let call_gas = call_ops as u64 * 100;
455    base + code_gas + storage_gas + call_gas
456}
457/// EVM version pass string
458#[allow(dead_code)]
459pub const EVM_BACKEND_PASS_VERSION: &str = "1.0.0";
460/// EVM deployment gas overhead
461#[allow(dead_code)]
462pub const EVM_DEPLOY_GAS_OVERHEAD: u64 = 32_000;
463/// EVM selector ABI encoding prefix
464#[allow(dead_code)]
465pub const EVM_ABI_SELECTOR_SIZE: usize = 4;
466#[cfg(test)]
467mod EVM_infra_tests {
468    use super::*;
469    #[test]
470    pub(super) fn test_pass_config() {
471        let config = EVMPassConfig::new("test_pass", EVMPassPhase::Transformation);
472        assert!(config.enabled);
473        assert!(config.phase.is_modifying());
474        assert_eq!(config.phase.name(), "transformation");
475    }
476    #[test]
477    pub(super) fn test_pass_stats() {
478        let mut stats = EVMPassStats::new();
479        stats.record_run(10, 100, 3);
480        stats.record_run(20, 200, 5);
481        assert_eq!(stats.total_runs, 2);
482        assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
483        assert!((stats.success_rate() - 1.0).abs() < 0.01);
484        let s = stats.format_summary();
485        assert!(s.contains("Runs: 2/2"));
486    }
487    #[test]
488    pub(super) fn test_pass_registry() {
489        let mut reg = EVMPassRegistry::new();
490        reg.register(EVMPassConfig::new("pass_a", EVMPassPhase::Analysis));
491        reg.register(EVMPassConfig::new("pass_b", EVMPassPhase::Transformation).disabled());
492        assert_eq!(reg.total_passes(), 2);
493        assert_eq!(reg.enabled_count(), 1);
494        reg.update_stats("pass_a", 5, 50, 2);
495        let stats = reg.get_stats("pass_a").expect("stats should exist");
496        assert_eq!(stats.total_changes, 5);
497    }
498    #[test]
499    pub(super) fn test_analysis_cache() {
500        let mut cache = EVMAnalysisCache::new(10);
501        cache.insert("key1".to_string(), vec![1, 2, 3]);
502        assert!(cache.get("key1").is_some());
503        assert!(cache.get("key2").is_none());
504        assert!((cache.hit_rate() - 0.5).abs() < 0.01);
505        cache.invalidate("key1");
506        assert!(!cache.entries["key1"].valid);
507        assert_eq!(cache.size(), 1);
508    }
509    #[test]
510    pub(super) fn test_worklist() {
511        let mut wl = EVMWorklist::new();
512        assert!(wl.push(1));
513        assert!(wl.push(2));
514        assert!(!wl.push(1));
515        assert_eq!(wl.len(), 2);
516        assert_eq!(wl.pop(), Some(1));
517        assert!(!wl.contains(1));
518        assert!(wl.contains(2));
519    }
520    #[test]
521    pub(super) fn test_dominator_tree() {
522        let mut dt = EVMDominatorTree::new(5);
523        dt.set_idom(1, 0);
524        dt.set_idom(2, 0);
525        dt.set_idom(3, 1);
526        assert!(dt.dominates(0, 3));
527        assert!(dt.dominates(1, 3));
528        assert!(!dt.dominates(2, 3));
529        assert!(dt.dominates(3, 3));
530    }
531    #[test]
532    pub(super) fn test_liveness() {
533        let mut liveness = EVMLivenessInfo::new(3);
534        liveness.add_def(0, 1);
535        liveness.add_use(1, 1);
536        assert!(liveness.defs[0].contains(&1));
537        assert!(liveness.uses[1].contains(&1));
538    }
539    #[test]
540    pub(super) fn test_constant_folding() {
541        assert_eq!(EVMConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
542        assert_eq!(EVMConstantFoldingHelper::fold_div_i64(10, 0), None);
543        assert_eq!(EVMConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
544        assert_eq!(
545            EVMConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
546            0b1000
547        );
548        assert_eq!(EVMConstantFoldingHelper::fold_bitnot_i64(0), -1);
549    }
550    #[test]
551    pub(super) fn test_dep_graph() {
552        let mut g = EVMDepGraph::new();
553        g.add_dep(1, 2);
554        g.add_dep(2, 3);
555        g.add_dep(1, 3);
556        assert_eq!(g.dependencies_of(2), vec![1]);
557        let topo = g.topological_sort();
558        assert_eq!(topo.len(), 3);
559        assert!(!g.has_cycle());
560        let pos: std::collections::HashMap<u32, usize> =
561            topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
562        assert!(pos[&1] < pos[&2]);
563        assert!(pos[&1] < pos[&3]);
564        assert!(pos[&2] < pos[&3]);
565    }
566}
567/// EVM identifier mangler
568#[allow(dead_code)]
569pub fn evm_mangle_identifier(name: &str) -> String {
570    name.chars()
571        .map(|c| {
572            if c.is_alphanumeric() || c == '_' {
573                c
574            } else {
575                '_'
576            }
577        })
578        .collect()
579}
580/// Compute the full 32-byte keccak256 hash of the given input bytes.
581/// Used internally for ABI selector computation and other EVM-level hashing.
582#[allow(dead_code)]
583pub fn evm_keccak256(input: &[u8]) -> [u8; 32] {
584    use tiny_keccak::{Hasher, Keccak};
585    let mut keccak = Keccak::v256();
586    let mut out = [0u8; 32];
587    keccak.update(input);
588    keccak.finalize(&mut out);
589    out
590}
591/// EVM is valid selector
592#[allow(dead_code)]
593pub fn evm_is_valid_selector(selector: &[u8]) -> bool {
594    selector.len() == 4
595}
596/// EVM code pass version
597#[allow(dead_code)]
598pub const EVM_CODE_PASS_VERSION: &str = "1.0.0";
599/// EVM solidity min version
600#[allow(dead_code)]
601pub const EVM_SOLIDITY_MIN_VERSION: &str = "0.8.0";