1use 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
16pub 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}