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_build_arithmetic_function() {
90        let sel = EvmBackend::compute_selector("add(uint256,uint256)");
91        let func = EvmBackend::build_arithmetic_function(
92            "add",
93            "add(uint256,uint256)",
94            sel,
95            EvmOpcode::Add,
96        );
97        assert_eq!(func.name, "add");
98        assert_eq!(func.selector, sel);
99        assert_eq!(func.blocks.len(), 1);
100        assert!(func.blocks[0].is_jump_target);
101        let bytes = func.encode();
102        assert!(!bytes.is_empty());
103        assert_eq!(
104            *bytes.last().expect("collection should not be empty"),
105            EvmOpcode::Return.byte()
106        );
107    }
108    #[test]
109    pub(super) fn test_emit_hex_and_assembly() {
110        let backend = EvmBackend::new();
111        let mut contract = EvmContract::new("SimpleToken");
112        contract.set_metadata("version", "0.8.0");
113        contract.allocate_storage("_balance");
114        contract.allocate_storage("_totalSupply");
115        let sel = EvmBackend::compute_selector("add(uint256,uint256)");
116        let func = EvmBackend::build_arithmetic_function(
117            "add",
118            "add(uint256,uint256)",
119            sel,
120            EvmOpcode::Add,
121        );
122        contract.add_function(func);
123        let hex = backend.emit_hex(&contract);
124        assert!(!hex.is_empty());
125        assert!(hex.chars().all(|c| c.is_ascii_hexdigit()));
126        let hex_prefixed = backend.emit_hex_prefixed(&contract);
127        assert!(hex_prefixed.starts_with("0x"));
128        let asm = backend.emit_assembly(&contract);
129        assert!(asm.contains("SimpleToken"));
130        assert!(asm.contains("CALLDATALOAD"));
131        assert!(asm.contains("_balance"));
132        assert!(asm.contains("_totalSupply"));
133        assert!(asm.contains("add"));
134    }
135}
136/// EVM standard opcode table
137#[allow(dead_code)]
138pub fn evm_opcode_table() -> Vec<EvmOpcodeDesc> {
139    vec![
140        EvmOpcodeDesc {
141            name: "STOP".into(),
142            opcode: 0x00,
143            stack_in: 0,
144            stack_out: 0,
145            gas: 0,
146            category: EvmOpcodeCategory::Stop,
147            description: "Halts execution".into(),
148        },
149        EvmOpcodeDesc {
150            name: "ADD".into(),
151            opcode: 0x01,
152            stack_in: 2,
153            stack_out: 1,
154            gas: 3,
155            category: EvmOpcodeCategory::Arithmetic,
156            description: "Addition".into(),
157        },
158        EvmOpcodeDesc {
159            name: "MUL".into(),
160            opcode: 0x02,
161            stack_in: 2,
162            stack_out: 1,
163            gas: 5,
164            category: EvmOpcodeCategory::Arithmetic,
165            description: "Multiplication".into(),
166        },
167        EvmOpcodeDesc {
168            name: "SUB".into(),
169            opcode: 0x03,
170            stack_in: 2,
171            stack_out: 1,
172            gas: 3,
173            category: EvmOpcodeCategory::Arithmetic,
174            description: "Subtraction".into(),
175        },
176        EvmOpcodeDesc {
177            name: "DIV".into(),
178            opcode: 0x04,
179            stack_in: 2,
180            stack_out: 1,
181            gas: 5,
182            category: EvmOpcodeCategory::Arithmetic,
183            description: "Integer division".into(),
184        },
185        EvmOpcodeDesc {
186            name: "MOD".into(),
187            opcode: 0x06,
188            stack_in: 2,
189            stack_out: 1,
190            gas: 5,
191            category: EvmOpcodeCategory::Arithmetic,
192            description: "Modulo".into(),
193        },
194        EvmOpcodeDesc {
195            name: "EXP".into(),
196            opcode: 0x0a,
197            stack_in: 2,
198            stack_out: 1,
199            gas: 10,
200            category: EvmOpcodeCategory::Arithmetic,
201            description: "Exponentiation".into(),
202        },
203        EvmOpcodeDesc {
204            name: "LT".into(),
205            opcode: 0x10,
206            stack_in: 2,
207            stack_out: 1,
208            gas: 3,
209            category: EvmOpcodeCategory::Comparison,
210            description: "Less than".into(),
211        },
212        EvmOpcodeDesc {
213            name: "GT".into(),
214            opcode: 0x11,
215            stack_in: 2,
216            stack_out: 1,
217            gas: 3,
218            category: EvmOpcodeCategory::Comparison,
219            description: "Greater than".into(),
220        },
221        EvmOpcodeDesc {
222            name: "EQ".into(),
223            opcode: 0x14,
224            stack_in: 2,
225            stack_out: 1,
226            gas: 3,
227            category: EvmOpcodeCategory::Comparison,
228            description: "Equality".into(),
229        },
230        EvmOpcodeDesc {
231            name: "ISZERO".into(),
232            opcode: 0x15,
233            stack_in: 1,
234            stack_out: 1,
235            gas: 3,
236            category: EvmOpcodeCategory::Comparison,
237            description: "Is zero".into(),
238        },
239        EvmOpcodeDesc {
240            name: "AND".into(),
241            opcode: 0x16,
242            stack_in: 2,
243            stack_out: 1,
244            gas: 3,
245            category: EvmOpcodeCategory::Bitwise,
246            description: "Bitwise AND".into(),
247        },
248        EvmOpcodeDesc {
249            name: "OR".into(),
250            opcode: 0x17,
251            stack_in: 2,
252            stack_out: 1,
253            gas: 3,
254            category: EvmOpcodeCategory::Bitwise,
255            description: "Bitwise OR".into(),
256        },
257        EvmOpcodeDesc {
258            name: "XOR".into(),
259            opcode: 0x18,
260            stack_in: 2,
261            stack_out: 1,
262            gas: 3,
263            category: EvmOpcodeCategory::Bitwise,
264            description: "Bitwise XOR".into(),
265        },
266        EvmOpcodeDesc {
267            name: "NOT".into(),
268            opcode: 0x19,
269            stack_in: 1,
270            stack_out: 1,
271            gas: 3,
272            category: EvmOpcodeCategory::Bitwise,
273            description: "Bitwise NOT".into(),
274        },
275        EvmOpcodeDesc {
276            name: "SHA3".into(),
277            opcode: 0x20,
278            stack_in: 2,
279            stack_out: 1,
280            gas: 30,
281            category: EvmOpcodeCategory::Sha3,
282            description: "Keccak-256".into(),
283        },
284        EvmOpcodeDesc {
285            name: "SLOAD".into(),
286            opcode: 0x54,
287            stack_in: 1,
288            stack_out: 1,
289            gas: 2100,
290            category: EvmOpcodeCategory::Storage,
291            description: "Load storage".into(),
292        },
293        EvmOpcodeDesc {
294            name: "SSTORE".into(),
295            opcode: 0x55,
296            stack_in: 2,
297            stack_out: 0,
298            gas: 20000,
299            category: EvmOpcodeCategory::Storage,
300            description: "Store to storage".into(),
301        },
302        EvmOpcodeDesc {
303            name: "JUMP".into(),
304            opcode: 0x56,
305            stack_in: 1,
306            stack_out: 0,
307            gas: 8,
308            category: EvmOpcodeCategory::Control,
309            description: "Unconditional jump".into(),
310        },
311        EvmOpcodeDesc {
312            name: "JUMPI".into(),
313            opcode: 0x57,
314            stack_in: 2,
315            stack_out: 0,
316            gas: 10,
317            category: EvmOpcodeCategory::Control,
318            description: "Conditional jump".into(),
319        },
320        EvmOpcodeDesc {
321            name: "RETURN".into(),
322            opcode: 0xf3,
323            stack_in: 2,
324            stack_out: 0,
325            gas: 0,
326            category: EvmOpcodeCategory::System,
327            description: "Return from call".into(),
328        },
329        EvmOpcodeDesc {
330            name: "REVERT".into(),
331            opcode: 0xfd,
332            stack_in: 2,
333            stack_out: 0,
334            gas: 0,
335            category: EvmOpcodeCategory::System,
336            description: "Revert".into(),
337        },
338        EvmOpcodeDesc {
339            name: "CALL".into(),
340            opcode: 0xf1,
341            stack_in: 7,
342            stack_out: 1,
343            gas: 100,
344            category: EvmOpcodeCategory::System,
345            description: "Message call".into(),
346        },
347        EvmOpcodeDesc {
348            name: "DELEGATECALL".into(),
349            opcode: 0xf4,
350            stack_in: 6,
351            stack_out: 1,
352            gas: 100,
353            category: EvmOpcodeCategory::System,
354            description: "Delegatecall".into(),
355        },
356        EvmOpcodeDesc {
357            name: "STATICCALL".into(),
358            opcode: 0xfa,
359            stack_in: 6,
360            stack_out: 1,
361            gas: 100,
362            category: EvmOpcodeCategory::System,
363            description: "Staticcall".into(),
364        },
365        EvmOpcodeDesc {
366            name: "CREATE".into(),
367            opcode: 0xf0,
368            stack_in: 3,
369            stack_out: 1,
370            gas: 32000,
371            category: EvmOpcodeCategory::System,
372            description: "Create contract".into(),
373        },
374        EvmOpcodeDesc {
375            name: "CREATE2".into(),
376            opcode: 0xf5,
377            stack_in: 4,
378            stack_out: 1,
379            gas: 32000,
380            category: EvmOpcodeCategory::System,
381            description: "Create2 contract".into(),
382        },
383    ]
384}
385/// EVM version string
386#[allow(dead_code)]
387pub const EVM_PASS_VERSION: &str = "1.0.0";
388/// EVM address size
389#[allow(dead_code)]
390pub const EVM_ADDRESS_SIZE_BYTES: usize = 20;
391/// EVM word size
392#[allow(dead_code)]
393pub const EVM_WORD_SIZE_BYTES: usize = 32;
394/// EVM stack max depth
395#[allow(dead_code)]
396pub const EVM_STACK_MAX_DEPTH: usize = 1024;
397/// EVM max code size (EIP-170)
398#[allow(dead_code)]
399pub const EVM_MAX_CODE_SIZE: usize = 24576;
400/// EVM max init code size (EIP-3860)
401#[allow(dead_code)]
402pub const EVM_MAX_INIT_CODE_SIZE: usize = 49152;
403/// EVM gas estimation
404#[allow(dead_code)]
405pub fn evm_estimate_gas(bytecode_len: usize, storage_ops: usize, call_ops: usize) -> u64 {
406    let base = 21_000u64;
407    let code_gas = (bytecode_len / 32) as u64 * 200;
408    let storage_gas = storage_ops as u64 * 20_000;
409    let call_gas = call_ops as u64 * 100;
410    base + code_gas + storage_gas + call_gas
411}
412/// EVM version pass string
413#[allow(dead_code)]
414pub const EVM_BACKEND_PASS_VERSION: &str = "1.0.0";
415/// EVM deployment gas overhead
416#[allow(dead_code)]
417pub const EVM_DEPLOY_GAS_OVERHEAD: u64 = 32_000;
418/// EVM selector ABI encoding prefix
419#[allow(dead_code)]
420pub const EVM_ABI_SELECTOR_SIZE: usize = 4;
421#[cfg(test)]
422mod EVM_infra_tests {
423    use super::*;
424    #[test]
425    pub(super) fn test_pass_config() {
426        let config = EVMPassConfig::new("test_pass", EVMPassPhase::Transformation);
427        assert!(config.enabled);
428        assert!(config.phase.is_modifying());
429        assert_eq!(config.phase.name(), "transformation");
430    }
431    #[test]
432    pub(super) fn test_pass_stats() {
433        let mut stats = EVMPassStats::new();
434        stats.record_run(10, 100, 3);
435        stats.record_run(20, 200, 5);
436        assert_eq!(stats.total_runs, 2);
437        assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
438        assert!((stats.success_rate() - 1.0).abs() < 0.01);
439        let s = stats.format_summary();
440        assert!(s.contains("Runs: 2/2"));
441    }
442    #[test]
443    pub(super) fn test_pass_registry() {
444        let mut reg = EVMPassRegistry::new();
445        reg.register(EVMPassConfig::new("pass_a", EVMPassPhase::Analysis));
446        reg.register(EVMPassConfig::new("pass_b", EVMPassPhase::Transformation).disabled());
447        assert_eq!(reg.total_passes(), 2);
448        assert_eq!(reg.enabled_count(), 1);
449        reg.update_stats("pass_a", 5, 50, 2);
450        let stats = reg.get_stats("pass_a").expect("stats should exist");
451        assert_eq!(stats.total_changes, 5);
452    }
453    #[test]
454    pub(super) fn test_analysis_cache() {
455        let mut cache = EVMAnalysisCache::new(10);
456        cache.insert("key1".to_string(), vec![1, 2, 3]);
457        assert!(cache.get("key1").is_some());
458        assert!(cache.get("key2").is_none());
459        assert!((cache.hit_rate() - 0.5).abs() < 0.01);
460        cache.invalidate("key1");
461        assert!(!cache.entries["key1"].valid);
462        assert_eq!(cache.size(), 1);
463    }
464    #[test]
465    pub(super) fn test_worklist() {
466        let mut wl = EVMWorklist::new();
467        assert!(wl.push(1));
468        assert!(wl.push(2));
469        assert!(!wl.push(1));
470        assert_eq!(wl.len(), 2);
471        assert_eq!(wl.pop(), Some(1));
472        assert!(!wl.contains(1));
473        assert!(wl.contains(2));
474    }
475    #[test]
476    pub(super) fn test_dominator_tree() {
477        let mut dt = EVMDominatorTree::new(5);
478        dt.set_idom(1, 0);
479        dt.set_idom(2, 0);
480        dt.set_idom(3, 1);
481        assert!(dt.dominates(0, 3));
482        assert!(dt.dominates(1, 3));
483        assert!(!dt.dominates(2, 3));
484        assert!(dt.dominates(3, 3));
485    }
486    #[test]
487    pub(super) fn test_liveness() {
488        let mut liveness = EVMLivenessInfo::new(3);
489        liveness.add_def(0, 1);
490        liveness.add_use(1, 1);
491        assert!(liveness.defs[0].contains(&1));
492        assert!(liveness.uses[1].contains(&1));
493    }
494    #[test]
495    pub(super) fn test_constant_folding() {
496        assert_eq!(EVMConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
497        assert_eq!(EVMConstantFoldingHelper::fold_div_i64(10, 0), None);
498        assert_eq!(EVMConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
499        assert_eq!(
500            EVMConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
501            0b1000
502        );
503        assert_eq!(EVMConstantFoldingHelper::fold_bitnot_i64(0), -1);
504    }
505    #[test]
506    pub(super) fn test_dep_graph() {
507        let mut g = EVMDepGraph::new();
508        g.add_dep(1, 2);
509        g.add_dep(2, 3);
510        g.add_dep(1, 3);
511        assert_eq!(g.dependencies_of(2), vec![1]);
512        let topo = g.topological_sort();
513        assert_eq!(topo.len(), 3);
514        assert!(!g.has_cycle());
515        let pos: std::collections::HashMap<u32, usize> =
516            topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
517        assert!(pos[&1] < pos[&2]);
518        assert!(pos[&1] < pos[&3]);
519        assert!(pos[&2] < pos[&3]);
520    }
521}
522/// EVM identifier mangler
523#[allow(dead_code)]
524pub fn evm_mangle_identifier(name: &str) -> String {
525    name.chars()
526        .map(|c| {
527            if c.is_alphanumeric() || c == '_' {
528                c
529            } else {
530                '_'
531            }
532        })
533        .collect()
534}
535/// EVM keccak mock (for selector computation placeholder)
536#[allow(dead_code)]
537pub fn evm_keccak_placeholder(input: &[u8]) -> [u8; 32] {
538    let mut out = [0u8; 32];
539    for (i, &b) in input.iter().take(32).enumerate() {
540        out[i] = b;
541    }
542    out
543}
544/// EVM is valid selector
545#[allow(dead_code)]
546pub fn evm_is_valid_selector(selector: &[u8]) -> bool {
547    selector.len() == 4
548}
549/// EVM code pass version
550#[allow(dead_code)]
551pub const EVM_CODE_PASS_VERSION: &str = "1.0.0";
552/// EVM solidity min version
553#[allow(dead_code)]
554pub const EVM_SOLIDITY_MIN_VERSION: &str = "0.8.0";