Skip to main content

oxilean_codegen/vyper_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    VypAnalysisCache, VypConstantFoldingHelper, VypDepGraph, VypDominatorTree, VypLivenessInfo,
9    VypPassConfig, VypPassPhase, VypPassRegistry, VypPassStats, VypWorklist, VyperBackend,
10    VyperConstant, VyperContract, VyperDecorator, VyperEvent, VyperExpr, VyperExtCache,
11    VyperExtConstFolder, VyperExtDepGraph, VyperExtDomTree, VyperExtLiveness, VyperExtPassConfig,
12    VyperExtPassPhase, VyperExtPassRegistry, VyperExtPassStats, VyperExtWorklist, VyperFlagDef,
13    VyperFunction, VyperInterface, VyperParam, VyperStmt, VyperStorageVar, VyperStruct, VyperType,
14};
15
16/// Standard runtime helpers emitted as Vyper module-level utilities.
17pub const VYPER_RUNTIME: &str = r#"# OxiLean Vyper Runtime Helpers
18# These @external functions expose common mathematical utilities.
19
20@external
21@view
22def oxilean_min(a: uint256, b: uint256) -> uint256:
23    """
24    @dev Returns the minimum of two uint256 values.
25    @param a First value.
26    @param b Second value.
27    @return Minimum of a and b.
28    """
29    if a < b:
30        return a
31    return b
32
33@external
34@view
35def oxilean_max(a: uint256, b: uint256) -> uint256:
36    """
37    @dev Returns the maximum of two uint256 values.
38    @param a First value.
39    @param b Second value.
40    @return Maximum of a and b.
41    """
42    if a > b:
43        return a
44    return b
45
46@external
47@pure
48def oxilean_abs_diff(a: uint256, b: uint256) -> uint256:
49    """
50    @dev Returns |a - b|.
51    """
52    if a >= b:
53        return a - b
54    return b - a
55
56@external
57@pure
58def oxilean_saturating_add(a: uint256, b: uint256) -> uint256:
59    """
60    @dev Saturating addition — returns max_value(uint256) on overflow.
61    """
62    result: uint256 = a + b
63    if result < a:
64        return max_value(uint256)
65    return result
66
67@external
68@pure
69def oxilean_saturating_sub(a: uint256, b: uint256) -> uint256:
70    """
71    @dev Saturating subtraction — clamps to 0 on underflow.
72    """
73    if a > b:
74        return a - b
75    return 0
76
77@external
78@pure
79def oxilean_clamp(value: uint256, lo: uint256, hi: uint256) -> uint256:
80    """
81    @dev Clamps value to [lo, hi].
82    """
83    if value < lo:
84        return lo
85    if value > hi:
86        return hi
87    return value
88
89@external
90@pure
91def oxilean_is_pow2(n: uint256) -> bool:
92    """
93    @dev Returns True iff n is a power of two (n > 0).
94    """
95    if n == 0:
96        return False
97    return (n & (n - 1)) == 0
98
99@external
100@pure
101def oxilean_log2_floor(n: uint256) -> uint256:
102    """
103    @dev Floor log base 2 of n (n must be > 0).
104    """
105    assert n > 0, "log2 of zero"
106    result: uint256 = 0
107    x: uint256 = n
108    for _: uint256 in range(256):
109        if x <= 1:
110            break
111        x = x >> 1
112        result += 1
113    return result
114
115@external
116@pure
117def oxilean_count_ones(n: uint256) -> uint256:
118    """
119    @dev Population count — number of set bits in n.
120    """
121    count: uint256 = 0
122    x: uint256 = n
123    for _: uint256 in range(256):
124        if x == 0:
125            break
126        count += x & 1
127        x = x >> 1
128    return count
129
130@external
131@view
132def oxilean_addr_to_uint(a: address) -> uint256:
133    """
134    @dev Converts an address to uint256.
135    """
136    return convert(a, uint256)
137
138@external
139@pure
140def oxilean_bytes32_to_bool(b: bytes32) -> bool:
141    """
142    @dev Returns True if any byte in b is non-zero.
143    """
144    return b != empty(bytes32)
145
146@external
147@view
148def oxilean_self_balance() -> uint256:
149    """
150    @dev Returns the current contract ETH balance.
151    """
152    return self.balance
153
154@external
155@view
156def oxilean_code_size(a: address) -> uint256:
157    """
158    @dev Returns the code size of an address (0 for EOA).
159    """
160    return a.codesize
161
162@external
163@pure
164def oxilean_pack_two_uint128(hi: uint128, lo: uint128) -> uint256:
165    """
166    @dev Packs two uint128 values into a single uint256.
167    """
168    return (convert(hi, uint256) << 128) | convert(lo, uint256)
169
170@external
171@pure
172def oxilean_unpack_hi(packed: uint256) -> uint128:
173    """
174    @dev Extracts the high 128 bits from a packed uint256.
175    """
176    return convert(packed >> 128, uint128)
177
178@external
179@pure
180def oxilean_unpack_lo(packed: uint256) -> uint128:
181    """
182    @dev Extracts the low 128 bits from a packed uint256.
183    """
184    return convert(packed & convert(max_value(uint128), uint256), uint128)
185"#;
186#[cfg(test)]
187mod tests {
188    use super::*;
189    #[test]
190    pub(super) fn test_uint256_display() {
191        assert_eq!(VyperType::Uint256.to_string(), "uint256");
192    }
193    #[test]
194    pub(super) fn test_hashmap_display() {
195        let ty = VyperType::HashMap(Box::new(VyperType::Address), Box::new(VyperType::Uint256));
196        assert_eq!(ty.to_string(), "HashMap[address, uint256]");
197    }
198    #[test]
199    pub(super) fn test_dyn_array_display() {
200        let ty = VyperType::DynArray(Box::new(VyperType::Uint256), 128);
201        assert_eq!(ty.to_string(), "DynArray[uint256, 128]");
202    }
203    #[test]
204    pub(super) fn test_fixed_array_display() {
205        let ty = VyperType::FixedArray(Box::new(VyperType::Bool), 10);
206        assert_eq!(ty.to_string(), "bool[10]");
207    }
208    #[test]
209    pub(super) fn test_bytes_bounded_display() {
210        assert_eq!(VyperType::Bytes(32).to_string(), "Bytes[32]");
211    }
212    #[test]
213    pub(super) fn test_string_bounded_display() {
214        assert_eq!(VyperType::StringTy(100).to_string(), "String[100]");
215    }
216    #[test]
217    pub(super) fn test_decimal_display() {
218        assert_eq!(VyperType::Decimal.to_string(), "decimal");
219    }
220    #[test]
221    pub(super) fn test_abi_canonical_dyn_array() {
222        let ty = VyperType::DynArray(Box::new(VyperType::Address), 10);
223        assert_eq!(ty.abi_canonical(), "address[]");
224    }
225    #[test]
226    pub(super) fn test_abi_canonical_string() {
227        assert_eq!(VyperType::StringTy(50).abi_canonical(), "string");
228    }
229    #[test]
230    pub(super) fn test_external_decorator() {
231        assert_eq!(VyperDecorator::External.to_string(), "@external");
232    }
233    #[test]
234    pub(super) fn test_nonreentrant_decorator() {
235        let d = VyperDecorator::NonReentrant("lock".into());
236        assert_eq!(d.to_string(), "@nonreentrant(\"lock\")");
237    }
238    #[test]
239    pub(super) fn test_view_decorator() {
240        assert_eq!(VyperDecorator::View.to_string(), "@view");
241    }
242    #[test]
243    pub(super) fn test_param_display_no_default() {
244        let p = VyperParam::new("amount", VyperType::Uint256);
245        assert_eq!(p.to_string(), "amount: uint256");
246    }
247    #[test]
248    pub(super) fn test_param_display_with_default() {
249        let p = VyperParam::new("decimals", VyperType::Uint8).with_default(VyperExpr::IntLit(18));
250        assert_eq!(p.to_string(), "decimals: uint8 = 18");
251    }
252    #[test]
253    pub(super) fn test_abi_signature() {
254        let mut func = VyperFunction::new("transfer");
255        func.params.push(VyperParam::new("to", VyperType::Address));
256        func.params
257            .push(VyperParam::new("amount", VyperType::Uint256));
258        assert_eq!(func.abi_signature(), "transfer(address,uint256)");
259    }
260    #[test]
261    pub(super) fn test_selector_length() {
262        let func = VyperFunction::new("balanceOf");
263        assert_eq!(func.selector().len(), 4);
264    }
265    #[test]
266    pub(super) fn test_selector_deterministic() {
267        let f1 = VyperFunction::new("approve");
268        let f2 = VyperFunction::new("approve");
269        assert_eq!(f1.selector(), f2.selector());
270    }
271    #[test]
272    pub(super) fn test_is_external() {
273        let func = VyperFunction::new("foo").external();
274        assert!(func.is_external());
275    }
276    #[test]
277    pub(super) fn test_is_read_only() {
278        let func = VyperFunction::new("foo").view();
279        assert!(func.is_read_only());
280        let func2 = VyperFunction::new("bar").pure_fn();
281        assert!(func2.is_read_only());
282    }
283    #[test]
284    pub(super) fn test_bool_lit_true() {
285        assert_eq!(VyperExpr::BoolLit(true).to_string(), "True");
286    }
287    #[test]
288    pub(super) fn test_bool_lit_false() {
289        assert_eq!(VyperExpr::BoolLit(false).to_string(), "False");
290    }
291    #[test]
292    pub(super) fn test_self_field() {
293        assert_eq!(
294            VyperExpr::SelfField("owner".into()).to_string(),
295            "self.owner"
296        );
297    }
298    #[test]
299    pub(super) fn test_if_expr_display() {
300        let expr = VyperExpr::IfExpr(
301            Box::new(VyperExpr::IntLit(1)),
302            Box::new(VyperExpr::BoolLit(true)),
303            Box::new(VyperExpr::IntLit(0)),
304        );
305        assert_eq!(expr.to_string(), "1 if True else 0");
306    }
307    #[test]
308    pub(super) fn test_convert_display() {
309        let expr = VyperExpr::Convert(Box::new(VyperExpr::MsgSender), VyperType::Uint256);
310        assert_eq!(expr.to_string(), "convert(msg.sender, uint256)");
311    }
312    #[test]
313    pub(super) fn test_empty_display() {
314        let expr = VyperExpr::Empty(VyperType::Uint256);
315        assert_eq!(expr.to_string(), "empty(uint256)");
316    }
317    #[test]
318    pub(super) fn test_max_value_display() {
319        let expr = VyperExpr::MaxValue(VyperType::Uint256);
320        assert_eq!(expr.to_string(), "max_value(uint256)");
321    }
322    #[test]
323    pub(super) fn test_binop_display() {
324        let expr = VyperExpr::BinOp(
325            "+".into(),
326            Box::new(VyperExpr::Var("a".into())),
327            Box::new(VyperExpr::IntLit(1)),
328        );
329        assert_eq!(expr.to_string(), "(a + 1)");
330    }
331    #[test]
332    pub(super) fn test_not_display() {
333        let expr = VyperExpr::UnaryOp("not".into(), Box::new(VyperExpr::BoolLit(false)));
334        assert_eq!(expr.to_string(), "not False");
335    }
336    #[test]
337    pub(super) fn test_emit_empty_contract() {
338        let mut backend = VyperBackend::new();
339        backend.set_contract(VyperContract::new("Empty"));
340        let src = backend.emit_contract();
341        assert!(src.contains("# @version"));
342        assert!(src.contains("0.3.10"));
343    }
344    #[test]
345    pub(super) fn test_emit_storage_var_public() {
346        let sv = VyperStorageVar {
347            name: "owner".into(),
348            ty: VyperType::Address,
349            is_public: true,
350            doc: None,
351        };
352        let out = VyperBackend::emit_storage_var(&sv);
353        assert!(out.contains("owner: address(public)"));
354    }
355    #[test]
356    pub(super) fn test_emit_event() {
357        let ev = VyperEvent {
358            name: "Transfer".into(),
359            fields: vec![
360                ("sender".into(), VyperType::Address, true),
361                ("receiver".into(), VyperType::Address, true),
362                ("value".into(), VyperType::Uint256, false),
363            ],
364            doc: None,
365        };
366        let out = VyperBackend::emit_event(&ev);
367        assert!(out.contains("event Transfer:"));
368        assert!(out.contains("sender: indexed(address)"));
369        assert!(out.contains("value: uint256"));
370    }
371    #[test]
372    pub(super) fn test_emit_struct() {
373        let s = VyperStruct {
374            name: "Point".into(),
375            fields: vec![
376                ("x".into(), VyperType::Int256),
377                ("y".into(), VyperType::Int256),
378            ],
379            doc: None,
380        };
381        let out = VyperBackend::emit_struct(&s);
382        assert!(out.contains("struct Point:"));
383        assert!(out.contains("x: int256"));
384    }
385    #[test]
386    pub(super) fn test_emit_constant() {
387        let c = VyperConstant {
388            name: "MAX_SUPPLY".into(),
389            ty: VyperType::Uint256,
390            value: VyperExpr::IntLit(1_000_000),
391            doc: None,
392        };
393        let out = VyperBackend::emit_constant(&c);
394        assert!(out.contains("MAX_SUPPLY: constant(uint256) = 1000000"));
395    }
396    #[test]
397    pub(super) fn test_emit_function_with_body() {
398        let mut func = VyperFunction::new("get_balance").external().view();
399        func.params
400            .push(VyperParam::new("addr", VyperType::Address));
401        func.return_ty = Some(VyperType::Uint256);
402        func.body.push(VyperStmt::Return(Some(VyperExpr::Index(
403            Box::new(VyperExpr::SelfField("balances".into())),
404            Box::new(VyperExpr::Var("addr".into())),
405        ))));
406        let out = VyperBackend::emit_function(&func, false);
407        assert!(out.contains("@external"));
408        assert!(out.contains("@view"));
409        assert!(out.contains("def get_balance(addr: address) -> uint256:"));
410        assert!(out.contains("return self.balances[addr]"));
411    }
412    #[test]
413    pub(super) fn test_emit_assert_stmt() {
414        let stmt = VyperStmt::Assert(
415            VyperExpr::BinOp(
416                ">".into(),
417                Box::new(VyperExpr::Var("amount".into())),
418                Box::new(VyperExpr::IntLit(0)),
419            ),
420            Some("Amount must be positive".into()),
421        );
422        let out = VyperBackend::emit_stmt(&stmt, 1);
423        assert!(out.contains("assert (amount > 0)"));
424        assert!(out.contains("Amount must be positive"));
425    }
426    #[test]
427    pub(super) fn test_emit_for_range_stmt() {
428        let stmt = VyperStmt::ForRange(
429            "i".into(),
430            VyperType::Uint256,
431            VyperExpr::IntLit(10),
432            vec![VyperStmt::Pass],
433        );
434        let out = VyperBackend::emit_stmt(&stmt, 1);
435        assert!(out.contains("for i: uint256 in range(10):"));
436        assert!(out.contains("pass"));
437    }
438    #[test]
439    pub(super) fn test_emit_log_stmt() {
440        let stmt = VyperStmt::Log(
441            "Transfer".into(),
442            vec![
443                VyperExpr::MsgSender,
444                VyperExpr::Var("to".into()),
445                VyperExpr::Var("amount".into()),
446            ],
447        );
448        let out = VyperBackend::emit_stmt(&stmt, 1);
449        assert!(out.contains("log Transfer(msg.sender, to, amount)"));
450    }
451    #[test]
452    pub(super) fn test_compile_decl() {
453        let backend = VyperBackend::new();
454        let sv = backend.compile_decl("total_supply", VyperType::Uint256);
455        assert_eq!(sv.name, "total_supply");
456        assert!(matches!(sv.ty, VyperType::Uint256));
457        assert!(!sv.is_public);
458    }
459    #[test]
460    pub(super) fn test_runtime_constant_not_empty() {
461        assert!(!VYPER_RUNTIME.is_empty());
462        assert!(VYPER_RUNTIME.contains("@external"));
463        assert!(VYPER_RUNTIME.contains("oxilean_min"));
464    }
465    #[test]
466    pub(super) fn test_emit_with_runtime() {
467        let mut backend = VyperBackend::new().with_runtime();
468        backend.set_contract(VyperContract::new("X"));
469        let src = backend.emit_contract();
470        assert!(src.contains("oxilean_min"));
471        assert!(src.contains("oxilean_saturating_add"));
472    }
473    #[test]
474    pub(super) fn test_with_version() {
475        let mut backend = VyperBackend::new().with_version("0.4.0");
476        backend.set_contract(VyperContract::new("X"));
477        let src = backend.emit_contract();
478        assert!(src.contains("# @version 0.4.0"));
479    }
480    #[test]
481    pub(super) fn test_emit_flag() {
482        let fl = VyperFlagDef {
483            name: "Roles".into(),
484            variants: vec!["ADMIN".into(), "MINTER".into(), "BURNER".into()],
485            doc: None,
486        };
487        let out = VyperBackend::emit_flag(&fl);
488        assert!(out.contains("flag Roles:"));
489        assert!(out.contains("ADMIN"));
490        assert!(out.contains("MINTER"));
491    }
492    #[test]
493    pub(super) fn test_emit_interface() {
494        let mut iface = VyperInterface {
495            name: "IERC20".into(),
496            functions: Vec::new(),
497            doc: None,
498        };
499        let mut func = VyperFunction::new("transfer");
500        func.decorators.push(VyperDecorator::External);
501        func.params.push(VyperParam::new("to", VyperType::Address));
502        func.params
503            .push(VyperParam::new("amount", VyperType::Uint256));
504        func.return_ty = Some(VyperType::Bool);
505        iface.functions.push(func);
506        let out = VyperBackend::emit_interface(&iface);
507        assert!(out.contains("interface IERC20:"));
508        assert!(out.contains("def transfer(to: address, amount: uint256) -> bool:"));
509    }
510    #[test]
511    pub(super) fn test_needs_init_hashmap() {
512        let ty = VyperType::HashMap(Box::new(VyperType::Address), Box::new(VyperType::Uint256));
513        assert!(ty.needs_init());
514    }
515    #[test]
516    pub(super) fn test_needs_init_uint256() {
517        assert!(!VyperType::Uint256.needs_init());
518    }
519    #[test]
520    pub(super) fn test_raw_call_display() {
521        let expr = VyperExpr::RawCall {
522            addr: Box::new(VyperExpr::Var("target".into())),
523            data: Box::new(VyperExpr::HexLit("0x".into())),
524            value: Some(Box::new(VyperExpr::IntLit(0))),
525            gas: None,
526        };
527        let s = expr.to_string();
528        assert!(s.contains("raw_call(target, 0x, value=0)"));
529    }
530    #[test]
531    pub(super) fn test_send_stmt() {
532        let stmt = VyperStmt::Send(VyperExpr::MsgSender, VyperExpr::IntLit(1000));
533        let out = VyperBackend::emit_stmt(&stmt, 1);
534        assert!(out.contains("send(msg.sender, 1000)"));
535    }
536    #[test]
537    pub(super) fn test_full_erc20_token_shape() {
538        let mut backend = VyperBackend::new();
539        let mut contract = VyperContract::new("MyToken");
540        contract.doc = Some("A simple ERC20 token".into());
541        contract.storage.push(VyperStorageVar {
542            name: "balanceOf".into(),
543            ty: VyperType::HashMap(Box::new(VyperType::Address), Box::new(VyperType::Uint256)),
544            is_public: true,
545            doc: None,
546        });
547        contract.storage.push(VyperStorageVar {
548            name: "totalSupply".into(),
549            ty: VyperType::Uint256,
550            is_public: true,
551            doc: None,
552        });
553        contract.events.push(VyperEvent {
554            name: "Transfer".into(),
555            fields: vec![
556                ("sender".into(), VyperType::Address, true),
557                ("receiver".into(), VyperType::Address, true),
558                ("value".into(), VyperType::Uint256, false),
559            ],
560            doc: None,
561        });
562        let mut transfer = VyperFunction::new("transfer")
563            .external()
564            .nonreentrant("lock");
565        transfer
566            .params
567            .push(VyperParam::new("receiver", VyperType::Address));
568        transfer
569            .params
570            .push(VyperParam::new("amount", VyperType::Uint256));
571        transfer.return_ty = Some(VyperType::Bool);
572        transfer.body.push(VyperStmt::Assert(
573            VyperExpr::BinOp(
574                ">=".into(),
575                Box::new(VyperExpr::Index(
576                    Box::new(VyperExpr::SelfField("balanceOf".into())),
577                    Box::new(VyperExpr::MsgSender),
578                )),
579                Box::new(VyperExpr::Var("amount".into())),
580            ),
581            Some("Insufficient balance".into()),
582        ));
583        transfer.body.push(VyperStmt::AugAssign(
584            "-".into(),
585            VyperExpr::Index(
586                Box::new(VyperExpr::SelfField("balanceOf".into())),
587                Box::new(VyperExpr::MsgSender),
588            ),
589            VyperExpr::Var("amount".into()),
590        ));
591        transfer.body.push(VyperStmt::AugAssign(
592            "+".into(),
593            VyperExpr::Index(
594                Box::new(VyperExpr::SelfField("balanceOf".into())),
595                Box::new(VyperExpr::Var("receiver".into())),
596            ),
597            VyperExpr::Var("amount".into()),
598        ));
599        transfer.body.push(VyperStmt::Log(
600            "Transfer".into(),
601            vec![
602                VyperExpr::MsgSender,
603                VyperExpr::Var("receiver".into()),
604                VyperExpr::Var("amount".into()),
605            ],
606        ));
607        transfer
608            .body
609            .push(VyperStmt::Return(Some(VyperExpr::BoolLit(true))));
610        contract.functions.push(transfer);
611        backend.set_contract(contract);
612        let src = backend.emit_contract();
613        assert!(src.contains("@version"));
614        assert!(src.contains("event Transfer:"));
615        assert!(src.contains("balanceOf: HashMap[address, uint256](public)"));
616        assert!(src.contains("@external"));
617        assert!(src.contains("@nonreentrant(\"lock\")"));
618        assert!(src.contains("def transfer(receiver: address, amount: uint256) -> bool:"));
619        assert!(src.contains("assert"));
620        assert!(src.contains("log Transfer("));
621        assert!(src.contains("return True"));
622    }
623}
624#[cfg(test)]
625mod Vyp_infra_tests {
626    use super::*;
627    #[test]
628    pub(super) fn test_pass_config() {
629        let config = VypPassConfig::new("test_pass", VypPassPhase::Transformation);
630        assert!(config.enabled);
631        assert!(config.phase.is_modifying());
632        assert_eq!(config.phase.name(), "transformation");
633    }
634    #[test]
635    pub(super) fn test_pass_stats() {
636        let mut stats = VypPassStats::new();
637        stats.record_run(10, 100, 3);
638        stats.record_run(20, 200, 5);
639        assert_eq!(stats.total_runs, 2);
640        assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
641        assert!((stats.success_rate() - 1.0).abs() < 0.01);
642        let s = stats.format_summary();
643        assert!(s.contains("Runs: 2/2"));
644    }
645    #[test]
646    pub(super) fn test_pass_registry() {
647        let mut reg = VypPassRegistry::new();
648        reg.register(VypPassConfig::new("pass_a", VypPassPhase::Analysis));
649        reg.register(VypPassConfig::new("pass_b", VypPassPhase::Transformation).disabled());
650        assert_eq!(reg.total_passes(), 2);
651        assert_eq!(reg.enabled_count(), 1);
652        reg.update_stats("pass_a", 5, 50, 2);
653        let stats = reg.get_stats("pass_a").expect("stats should exist");
654        assert_eq!(stats.total_changes, 5);
655    }
656    #[test]
657    pub(super) fn test_analysis_cache() {
658        let mut cache = VypAnalysisCache::new(10);
659        cache.insert("key1".to_string(), vec![1, 2, 3]);
660        assert!(cache.get("key1").is_some());
661        assert!(cache.get("key2").is_none());
662        assert!((cache.hit_rate() - 0.5).abs() < 0.01);
663        cache.invalidate("key1");
664        assert!(!cache.entries["key1"].valid);
665        assert_eq!(cache.size(), 1);
666    }
667    #[test]
668    pub(super) fn test_worklist() {
669        let mut wl = VypWorklist::new();
670        assert!(wl.push(1));
671        assert!(wl.push(2));
672        assert!(!wl.push(1));
673        assert_eq!(wl.len(), 2);
674        assert_eq!(wl.pop(), Some(1));
675        assert!(!wl.contains(1));
676        assert!(wl.contains(2));
677    }
678    #[test]
679    pub(super) fn test_dominator_tree() {
680        let mut dt = VypDominatorTree::new(5);
681        dt.set_idom(1, 0);
682        dt.set_idom(2, 0);
683        dt.set_idom(3, 1);
684        assert!(dt.dominates(0, 3));
685        assert!(dt.dominates(1, 3));
686        assert!(!dt.dominates(2, 3));
687        assert!(dt.dominates(3, 3));
688    }
689    #[test]
690    pub(super) fn test_liveness() {
691        let mut liveness = VypLivenessInfo::new(3);
692        liveness.add_def(0, 1);
693        liveness.add_use(1, 1);
694        assert!(liveness.defs[0].contains(&1));
695        assert!(liveness.uses[1].contains(&1));
696    }
697    #[test]
698    pub(super) fn test_constant_folding() {
699        assert_eq!(VypConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
700        assert_eq!(VypConstantFoldingHelper::fold_div_i64(10, 0), None);
701        assert_eq!(VypConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
702        assert_eq!(
703            VypConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
704            0b1000
705        );
706        assert_eq!(VypConstantFoldingHelper::fold_bitnot_i64(0), -1);
707    }
708    #[test]
709    pub(super) fn test_dep_graph() {
710        let mut g = VypDepGraph::new();
711        g.add_dep(1, 2);
712        g.add_dep(2, 3);
713        g.add_dep(1, 3);
714        assert_eq!(g.dependencies_of(2), vec![1]);
715        let topo = g.topological_sort();
716        assert_eq!(topo.len(), 3);
717        assert!(!g.has_cycle());
718        let pos: std::collections::HashMap<u32, usize> =
719            topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
720        assert!(pos[&1] < pos[&2]);
721        assert!(pos[&1] < pos[&3]);
722        assert!(pos[&2] < pos[&3]);
723    }
724}
725#[cfg(test)]
726mod vyperext_pass_tests {
727    use super::*;
728    #[test]
729    pub(super) fn test_vyperext_phase_order() {
730        assert_eq!(VyperExtPassPhase::Early.order(), 0);
731        assert_eq!(VyperExtPassPhase::Middle.order(), 1);
732        assert_eq!(VyperExtPassPhase::Late.order(), 2);
733        assert_eq!(VyperExtPassPhase::Finalize.order(), 3);
734        assert!(VyperExtPassPhase::Early.is_early());
735        assert!(!VyperExtPassPhase::Early.is_late());
736    }
737    #[test]
738    pub(super) fn test_vyperext_config_builder() {
739        let c = VyperExtPassConfig::new("p")
740            .with_phase(VyperExtPassPhase::Late)
741            .with_max_iter(50)
742            .with_debug(1);
743        assert_eq!(c.name, "p");
744        assert_eq!(c.max_iterations, 50);
745        assert!(c.is_debug_enabled());
746        assert!(c.enabled);
747        let c2 = c.disabled();
748        assert!(!c2.enabled);
749    }
750    #[test]
751    pub(super) fn test_vyperext_stats() {
752        let mut s = VyperExtPassStats::new();
753        s.visit();
754        s.visit();
755        s.modify();
756        s.iterate();
757        assert_eq!(s.nodes_visited, 2);
758        assert_eq!(s.nodes_modified, 1);
759        assert!(s.changed);
760        assert_eq!(s.iterations, 1);
761        let e = s.efficiency();
762        assert!((e - 0.5).abs() < 1e-9);
763    }
764    #[test]
765    pub(super) fn test_vyperext_registry() {
766        let mut r = VyperExtPassRegistry::new();
767        r.register(VyperExtPassConfig::new("a").with_phase(VyperExtPassPhase::Early));
768        r.register(VyperExtPassConfig::new("b").disabled());
769        assert_eq!(r.len(), 2);
770        assert_eq!(r.enabled_passes().len(), 1);
771        assert_eq!(r.passes_in_phase(&VyperExtPassPhase::Early).len(), 1);
772    }
773    #[test]
774    pub(super) fn test_vyperext_cache() {
775        let mut c = VyperExtCache::new(4);
776        assert!(c.get(99).is_none());
777        c.put(99, vec![1, 2, 3]);
778        let v = c.get(99).expect("v should be present in map");
779        assert_eq!(v, &[1u8, 2, 3]);
780        assert!(c.hit_rate() > 0.0);
781        assert_eq!(c.live_count(), 1);
782    }
783    #[test]
784    pub(super) fn test_vyperext_worklist() {
785        let mut w = VyperExtWorklist::new(10);
786        w.push(5);
787        w.push(3);
788        w.push(5);
789        assert_eq!(w.len(), 2);
790        assert!(w.contains(5));
791        let first = w.pop().expect("first should be available to pop");
792        assert!(!w.contains(first));
793    }
794    #[test]
795    pub(super) fn test_vyperext_dom_tree() {
796        let mut dt = VyperExtDomTree::new(5);
797        dt.set_idom(1, 0);
798        dt.set_idom(2, 0);
799        dt.set_idom(3, 1);
800        dt.set_idom(4, 1);
801        assert!(dt.dominates(0, 3));
802        assert!(dt.dominates(1, 4));
803        assert!(!dt.dominates(2, 3));
804        assert_eq!(dt.depth_of(3), 2);
805    }
806    #[test]
807    pub(super) fn test_vyperext_liveness() {
808        let mut lv = VyperExtLiveness::new(3);
809        lv.add_def(0, 1);
810        lv.add_use(1, 1);
811        assert!(lv.var_is_def_in_block(0, 1));
812        assert!(lv.var_is_used_in_block(1, 1));
813        assert!(!lv.var_is_def_in_block(1, 1));
814    }
815    #[test]
816    pub(super) fn test_vyperext_const_folder() {
817        let mut cf = VyperExtConstFolder::new();
818        assert_eq!(cf.add_i64(3, 4), Some(7));
819        assert_eq!(cf.div_i64(10, 0), None);
820        assert_eq!(cf.mul_i64(6, 7), Some(42));
821        assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
822        assert_eq!(cf.fold_count(), 3);
823        assert_eq!(cf.failure_count(), 1);
824    }
825    #[test]
826    pub(super) fn test_vyperext_dep_graph() {
827        let mut g = VyperExtDepGraph::new(4);
828        g.add_edge(0, 1);
829        g.add_edge(1, 2);
830        g.add_edge(2, 3);
831        assert!(!g.has_cycle());
832        assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
833        assert_eq!(g.reachable(0).len(), 4);
834        let sccs = g.scc();
835        assert_eq!(sccs.len(), 4);
836    }
837}