|
|
@@ -0,0 +1,1549 @@
|
|
|
+// SPDX-License-Identifier: Apache-2.0
|
|
|
+
|
|
|
+use std::ffi::OsStr;
|
|
|
+
|
|
|
+use solang::{
|
|
|
+ codegen::codegen,
|
|
|
+ file_resolver::FileResolver,
|
|
|
+ lir::{converter::Converter, printer::Printer},
|
|
|
+ parse_and_resolve,
|
|
|
+ sema::ast::Namespace,
|
|
|
+ Target,
|
|
|
+};
|
|
|
+
|
|
|
+use crate::stringfy_lir;
|
|
|
+
|
|
|
+fn new_file_resolver(src: &str) -> FileResolver {
|
|
|
+ let mut cache = FileResolver::default();
|
|
|
+ cache.set_file_contents("test.sol", src.to_string());
|
|
|
+ cache
|
|
|
+}
|
|
|
+
|
|
|
+fn print_lir_str(src: &str, cfg_no: usize, target: Target) {
|
|
|
+ let mut resolver = new_file_resolver(src);
|
|
|
+ let mut ns: Namespace = parse_and_resolve(OsStr::new("test.sol"), &mut resolver, target);
|
|
|
+ // print diagnostics
|
|
|
+ if !ns.diagnostics.is_empty() {
|
|
|
+ ns.print_diagnostics_in_plain(&resolver, false);
|
|
|
+ }
|
|
|
+ codegen(&mut ns, &Default::default());
|
|
|
+ let contract = ns.contracts.get(0).unwrap();
|
|
|
+ let cfg = contract.cfg.get(cfg_no).unwrap();
|
|
|
+
|
|
|
+ let converter = Converter::new(&ns, cfg);
|
|
|
+ let lir = converter.get_lir();
|
|
|
+
|
|
|
+ let printer = Printer::new(&lir.vartable);
|
|
|
+
|
|
|
+ let result = stringfy_lir!(printer, &lir);
|
|
|
+ println!("{}", result);
|
|
|
+}
|
|
|
+
|
|
|
+fn assert_lir_str_eq_by_name(src: &str, cfg_name: &str, expected: &str, target: Target) {
|
|
|
+ let mut resolver = new_file_resolver(src);
|
|
|
+ let mut ns: Namespace = parse_and_resolve(OsStr::new("test.sol"), &mut resolver, target);
|
|
|
+ // print diagnostics
|
|
|
+ if !ns.diagnostics.is_empty() {
|
|
|
+ ns.print_diagnostics_in_plain(&resolver, false);
|
|
|
+ }
|
|
|
+ codegen(&mut ns, &Default::default());
|
|
|
+ let contract = ns.contracts.get(0).unwrap();
|
|
|
+ let cfg = contract
|
|
|
+ .cfg
|
|
|
+ .iter()
|
|
|
+ .filter(|cfg| cfg.name == cfg_name)
|
|
|
+ .last()
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ let converter = Converter::new(&ns, cfg);
|
|
|
+ let lir = converter.get_lir();
|
|
|
+
|
|
|
+ let printer = Printer::new(&lir.vartable);
|
|
|
+
|
|
|
+ let result = stringfy_lir!(printer, &lir);
|
|
|
+ assert_eq!(result.trim(), expected);
|
|
|
+
|
|
|
+ let re = regex::Regex::new(r"%temp\.ssa_ir\.\d+ =").unwrap();
|
|
|
+ let mut temp_vars = Vec::new();
|
|
|
+ for cap in re.captures_iter(result.as_str()) {
|
|
|
+ temp_vars.push(cap[0].to_string());
|
|
|
+ }
|
|
|
+ // check if there are duplicated temp variables
|
|
|
+ let mut temp_vars_clone = temp_vars.clone();
|
|
|
+ temp_vars_clone.dedup();
|
|
|
+
|
|
|
+ // assert length equal
|
|
|
+ assert_eq!(temp_vars.len(), temp_vars_clone.len());
|
|
|
+}
|
|
|
+
|
|
|
+fn assert_lir_str_eq(src: &str, cfg_no: usize, expected: &str, target: Target) {
|
|
|
+ let mut resolver = new_file_resolver(src);
|
|
|
+ let mut ns: Namespace = parse_and_resolve(OsStr::new("test.sol"), &mut resolver, target);
|
|
|
+ // print diagnostics
|
|
|
+ if !ns.diagnostics.is_empty() {
|
|
|
+ ns.print_diagnostics_in_plain(&resolver, false);
|
|
|
+ }
|
|
|
+ codegen(&mut ns, &Default::default());
|
|
|
+ let contract = ns.contracts.get(0).unwrap();
|
|
|
+ let cfg = contract.cfg.get(cfg_no).unwrap();
|
|
|
+
|
|
|
+ let converter = Converter::new(&ns, cfg);
|
|
|
+ let lir = converter.get_lir();
|
|
|
+
|
|
|
+ let printer = Printer::new(&lir.vartable);
|
|
|
+
|
|
|
+ let result = stringfy_lir!(printer, &lir);
|
|
|
+ assert_eq!(result.trim(), expected);
|
|
|
+
|
|
|
+ let re = regex::Regex::new(r"%temp\.ssa_ir\.\d+ =").unwrap();
|
|
|
+ let mut temp_vars = Vec::new();
|
|
|
+ for cap in re.captures_iter(result.as_str()) {
|
|
|
+ temp_vars.push(cap[0].to_string());
|
|
|
+ }
|
|
|
+ // check if there are duplicated temp variables
|
|
|
+ let mut temp_vars_clone = temp_vars.clone();
|
|
|
+ temp_vars_clone.dedup();
|
|
|
+
|
|
|
+ // assert length equal
|
|
|
+ assert_eq!(temp_vars.len(), temp_vars_clone.len());
|
|
|
+}
|
|
|
+
|
|
|
+fn assert_solana_lir_str_eq(src: &str, cfg_no: usize, expected: &str) {
|
|
|
+ assert_lir_str_eq(src, cfg_no, expected, Target::Solana);
|
|
|
+}
|
|
|
+
|
|
|
+fn assert_polkadot_lir_str_eq(src: &str, cfg_no: usize, expected: &str) {
|
|
|
+ assert_lir_str_eq(src, cfg_no, expected, Target::default_polkadot());
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_convert_lir() {
|
|
|
+ let src = r#"
|
|
|
+contract dynamicarray {
|
|
|
+ function test() public pure {
|
|
|
+ int64[] memory a = new int64[](3);
|
|
|
+ a[0] = 1;
|
|
|
+ a[1] = 2;
|
|
|
+ a[2] = 3;
|
|
|
+ a.push(4);
|
|
|
+
|
|
|
+ assert(a.length == 4);
|
|
|
+ }
|
|
|
+}"#;
|
|
|
+
|
|
|
+ print_lir_str(src, 0, Target::Solana);
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 dynamicarray::dynamicarray::function::test ():
|
|
|
+block#0 entry:
|
|
|
+ uint32 %array_length.temp.1 = 3;
|
|
|
+ ptr<int64[]> %a = alloc ptr<int64[]>[uint32(3)];
|
|
|
+ uint32 %index.temp.3 = 0;
|
|
|
+ bool %temp.ssa_ir.9 = uint32(0) (u)>= uint32(3);
|
|
|
+ cbr bool(%temp.ssa_ir.9) block#1 else block#2;
|
|
|
+
|
|
|
+block#1 out_of_bounds:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#2 in_bounds:
|
|
|
+ int64 %temp.2 = 1;
|
|
|
+ ptr<int64> %temp.ssa_ir.10 = ptr<int64[]>(%a)[uint32(0)];
|
|
|
+ store int64(1) to ptr<int64>(%temp.ssa_ir.10);
|
|
|
+ uint32 %index.temp.5 = 1;
|
|
|
+ bool %temp.ssa_ir.11 = uint32(1) (u)>= uint32(3);
|
|
|
+ cbr bool(%temp.ssa_ir.11) block#3 else block#4;
|
|
|
+
|
|
|
+block#3 out_of_bounds:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#4 in_bounds:
|
|
|
+ int64 %temp.4 = 2;
|
|
|
+ ptr<int64> %temp.ssa_ir.12 = ptr<int64[]>(%a)[uint32(1)];
|
|
|
+ store int64(2) to ptr<int64>(%temp.ssa_ir.12);
|
|
|
+ uint32 %index.temp.7 = 2;
|
|
|
+ bool %temp.ssa_ir.13 = uint32(2) (u)>= uint32(3);
|
|
|
+ cbr bool(%temp.ssa_ir.13) block#5 else block#6;
|
|
|
+
|
|
|
+block#5 out_of_bounds:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#6 in_bounds:
|
|
|
+ int64 %temp.6 = 3;
|
|
|
+ ptr<int64> %temp.ssa_ir.14 = ptr<int64[]>(%a)[uint32(2)];
|
|
|
+ store int64(3) to ptr<int64>(%temp.ssa_ir.14);
|
|
|
+ int64 %temp.8 = push_mem ptr<int64[]>(%a) int64(4);
|
|
|
+ uint32 %array_length.temp.1 = 4;
|
|
|
+ bool %temp.ssa_ir.15 = uint32(4) == uint32(4);
|
|
|
+ cbr bool(%temp.ssa_ir.15) block#7 else block#8;
|
|
|
+
|
|
|
+block#7 noassert:
|
|
|
+ return;
|
|
|
+
|
|
|
+block#8 doassert:
|
|
|
+ assert_failure;"#,
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_bool_exprs() {
|
|
|
+ let cfg_no = 1;
|
|
|
+
|
|
|
+ // read the example.sol file
|
|
|
+ let src = r#"
|
|
|
+ contract test {
|
|
|
+ enum State {
|
|
|
+ Running,
|
|
|
+ Sleeping,
|
|
|
+ Waiting,
|
|
|
+ Stopped,
|
|
|
+ Zombie,
|
|
|
+ StateCount
|
|
|
+ }
|
|
|
+ State state;
|
|
|
+ int32 pid;
|
|
|
+ int32 constant first_pid = 1;
|
|
|
+ constructor(int32 _pid) {
|
|
|
+ pid = _pid;
|
|
|
+ }
|
|
|
+ function is_zombie_reaper() public view returns (bool) {
|
|
|
+ return (pid == first_pid && state != State.Zombie);
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ cfg_no,
|
|
|
+ r#"public function sol#3 test::test::function::is_zombie_reaper () returns (bool):
|
|
|
+block#0 entry:
|
|
|
+ int32 %temp.3 = load_storage uint32(20);
|
|
|
+ bool %and.temp.4 = false;
|
|
|
+ bool %temp.ssa_ir.6 = int32(%temp.3) == int32(1);
|
|
|
+ cbr bool(%temp.ssa_ir.6) block#1 else block#2;
|
|
|
+
|
|
|
+block#1 and_right_side:
|
|
|
+ uint8 %temp.5 = load_storage uint32(16);
|
|
|
+ bool %and.temp.4 = uint8(%temp.5) != uint8(4);
|
|
|
+ br block#2;
|
|
|
+
|
|
|
+block#2 and_end:
|
|
|
+ return bool(%and.temp.4);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_cast() {
|
|
|
+ let cfg_no = 0;
|
|
|
+
|
|
|
+ // read the example.sol file
|
|
|
+ let src = r#"
|
|
|
+ contract test {
|
|
|
+ int32 constant first_pid = 1;
|
|
|
+ function systemd_pid() public pure returns (uint32) {
|
|
|
+ return uint32(first_pid);
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ cfg_no,
|
|
|
+ r#"public function sol#2 test::test::function::systemd_pid () returns (uint32):
|
|
|
+block#0 entry:
|
|
|
+ uint32 %temp.ssa_ir.1 = (cast int32(1) to uint32);
|
|
|
+ return uint32(%temp.ssa_ir.1);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_arithmetic_exprs() {
|
|
|
+ let cfg_no = 0;
|
|
|
+
|
|
|
+ // read the example.sol file
|
|
|
+ let src = r#"
|
|
|
+ contract test {
|
|
|
+ function celcius2fahrenheit(int32 celcius) pure public returns (int32) {
|
|
|
+ int32 fahrenheit = celcius * 9 / 5 + 32;
|
|
|
+ return fahrenheit;
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ cfg_no,
|
|
|
+ r#"public function sol#2 test::test::function::celcius2fahrenheit__int32 (int32) returns (int32):
|
|
|
+block#0 entry:
|
|
|
+ int32 %celcius = int32(arg#0);
|
|
|
+ int32 %temp.ssa_ir.4 = int32(%celcius) * int32(9);
|
|
|
+ int32 %temp.ssa_ir.3 = int32(%temp.ssa_ir.4) / int32(5);
|
|
|
+ int32 %fahrenheit = int32(%temp.ssa_ir.3) + int32(32);
|
|
|
+ return int32(%fahrenheit);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_arithmetic_exprs_1() {
|
|
|
+ let cfg_no = 0;
|
|
|
+
|
|
|
+ // read the example.sol file
|
|
|
+ let src = r#"
|
|
|
+ contract test {
|
|
|
+ function byte8reverse(bytes8 input) public pure returns (bytes8 out) {
|
|
|
+ out = ((input << 56) & hex"ff00_0000_0000_0000") |
|
|
|
+ ((input << 40) & hex"00ff_0000_0000_0000") |
|
|
|
+ ((input << 24) & hex"0000_ff00_0000_0000") |
|
|
|
+ ((input << 8) & hex"0000_00ff_0000_0000") |
|
|
|
+ ((input >> 8) & hex"0000_0000_ff00_0000") |
|
|
|
+ ((input >> 24) & hex"0000_0000_00ff_0000") |
|
|
|
+ ((input >> 40) & hex"0000_0000_0000_ff00") |
|
|
|
+ ((input >> 56) & hex"0000_0000_0000_00ff");
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ cfg_no,
|
|
|
+ r#"public function sol#2 test::test::function::byte8reverse__bytes8 (bytes8) returns (bytes8):
|
|
|
+block#0 entry:
|
|
|
+ bytes8 %input = bytes8(arg#0);
|
|
|
+ bytes8 %out = bytes8 hex"00_00_00_00_00_00_00_00";
|
|
|
+ bytes8 %temp.ssa_ir.9 = bytes8(%input) << bytes8(56);
|
|
|
+ bytes8 %temp.ssa_ir.10 = bytes8 hex"ff_00_00_00_00_00_00_00";
|
|
|
+ bytes8 %temp.ssa_ir.8 = bytes8(%temp.ssa_ir.9) & bytes8(%temp.ssa_ir.10);
|
|
|
+ bytes8 %temp.ssa_ir.12 = bytes8(%input) << bytes8(40);
|
|
|
+ bytes8 %temp.ssa_ir.13 = bytes8 hex"00_ff_00_00_00_00_00_00";
|
|
|
+ bytes8 %temp.ssa_ir.11 = bytes8(%temp.ssa_ir.12) & bytes8(%temp.ssa_ir.13);
|
|
|
+ bytes8 %temp.ssa_ir.7 = bytes8(%temp.ssa_ir.8) | bytes8(%temp.ssa_ir.11);
|
|
|
+ bytes8 %temp.ssa_ir.15 = bytes8(%input) << bytes8(24);
|
|
|
+ bytes8 %temp.ssa_ir.16 = bytes8 hex"00_00_ff_00_00_00_00_00";
|
|
|
+ bytes8 %temp.ssa_ir.14 = bytes8(%temp.ssa_ir.15) & bytes8(%temp.ssa_ir.16);
|
|
|
+ bytes8 %temp.ssa_ir.6 = bytes8(%temp.ssa_ir.7) | bytes8(%temp.ssa_ir.14);
|
|
|
+ bytes8 %temp.ssa_ir.18 = bytes8(%input) << bytes8(8);
|
|
|
+ bytes8 %temp.ssa_ir.19 = bytes8 hex"00_00_00_ff_00_00_00_00";
|
|
|
+ bytes8 %temp.ssa_ir.17 = bytes8(%temp.ssa_ir.18) & bytes8(%temp.ssa_ir.19);
|
|
|
+ bytes8 %temp.ssa_ir.5 = bytes8(%temp.ssa_ir.6) | bytes8(%temp.ssa_ir.17);
|
|
|
+ bytes8 %temp.ssa_ir.21 = bytes8(%input) (u)>> bytes8(8);
|
|
|
+ bytes8 %temp.ssa_ir.22 = bytes8 hex"00_00_00_00_ff_00_00_00";
|
|
|
+ bytes8 %temp.ssa_ir.20 = bytes8(%temp.ssa_ir.21) & bytes8(%temp.ssa_ir.22);
|
|
|
+ bytes8 %temp.ssa_ir.4 = bytes8(%temp.ssa_ir.5) | bytes8(%temp.ssa_ir.20);
|
|
|
+ bytes8 %temp.ssa_ir.24 = bytes8(%input) (u)>> bytes8(24);
|
|
|
+ bytes8 %temp.ssa_ir.25 = bytes8 hex"00_00_00_00_00_ff_00_00";
|
|
|
+ bytes8 %temp.ssa_ir.23 = bytes8(%temp.ssa_ir.24) & bytes8(%temp.ssa_ir.25);
|
|
|
+ bytes8 %temp.ssa_ir.3 = bytes8(%temp.ssa_ir.4) | bytes8(%temp.ssa_ir.23);
|
|
|
+ bytes8 %temp.ssa_ir.27 = bytes8(%input) (u)>> bytes8(40);
|
|
|
+ bytes8 %temp.ssa_ir.28 = bytes8 hex"00_00_00_00_00_00_ff_00";
|
|
|
+ bytes8 %temp.ssa_ir.26 = bytes8(%temp.ssa_ir.27) & bytes8(%temp.ssa_ir.28);
|
|
|
+ bytes8 %temp.ssa_ir.2 = bytes8(%temp.ssa_ir.3) | bytes8(%temp.ssa_ir.26);
|
|
|
+ bytes8 %temp.ssa_ir.30 = bytes8(%input) (u)>> bytes8(56);
|
|
|
+ bytes8 %temp.ssa_ir.31 = bytes8 hex"00_00_00_00_00_00_00_ff";
|
|
|
+ bytes8 %temp.ssa_ir.29 = bytes8(%temp.ssa_ir.30) & bytes8(%temp.ssa_ir.31);
|
|
|
+ bytes8 %out = bytes8(%temp.ssa_ir.2) | bytes8(%temp.ssa_ir.29);
|
|
|
+ return bytes8(%out);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_for_loop() {
|
|
|
+ let cfg_no = 0;
|
|
|
+
|
|
|
+ // read the example.sol file
|
|
|
+ let src = r#"
|
|
|
+ contract test {
|
|
|
+ enum State {
|
|
|
+ Running,
|
|
|
+ Sleeping,
|
|
|
+ Waiting,
|
|
|
+ Stopped,
|
|
|
+ Zombie,
|
|
|
+ StateCount
|
|
|
+ }
|
|
|
+ function get_pid_state(uint64 _pid) pure private returns (State) {
|
|
|
+ uint64 n = 8;
|
|
|
+ for (uint16 i = 1; i < 10; ++i) {
|
|
|
+ if ((i % 3) == 0) {
|
|
|
+ n *= _pid / uint64(i);
|
|
|
+ } else {
|
|
|
+ n /= 3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return State(n % uint64(State.StateCount));
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ cfg_no,
|
|
|
+ r#"private function sol#2 test::test::function::get_pid_state__uint64 (uint64) returns (uint8):
|
|
|
+block#0 entry:
|
|
|
+ uint64 %_pid = uint64(arg#0);
|
|
|
+ uint64 %n = 8;
|
|
|
+ uint16 %i = 1;
|
|
|
+ br block#2;
|
|
|
+
|
|
|
+block#1 body:
|
|
|
+ uint16 %temp.ssa_ir.6 = uint16(%i) (u)% uint16(3);
|
|
|
+ bool %temp.ssa_ir.5 = uint16(%temp.ssa_ir.6) == uint16(0);
|
|
|
+ cbr bool(%temp.ssa_ir.5) block#5 else block#6;
|
|
|
+
|
|
|
+block#2 cond:
|
|
|
+ bool %temp.ssa_ir.7 = uint16(%i) (u)< uint16(10);
|
|
|
+ cbr bool(%temp.ssa_ir.7) block#1 else block#4;
|
|
|
+
|
|
|
+block#3 next:
|
|
|
+ uint16 %temp.4 = uint16(%i) + uint16(1);
|
|
|
+ uint16 %i = uint16(%temp.4);
|
|
|
+ br block#2;
|
|
|
+
|
|
|
+block#4 endfor:
|
|
|
+ uint64 %temp.ssa_ir.9 = uint64(%n) (u)% uint64(5);
|
|
|
+ uint8 %temp.ssa_ir.8 = (trunc uint64(%temp.ssa_ir.9) to uint8);
|
|
|
+ return uint8(%temp.ssa_ir.8);
|
|
|
+
|
|
|
+block#5 then:
|
|
|
+ uint64 %temp.ssa_ir.11 = (zext uint16(%i) to uint64);
|
|
|
+ uint64 %temp.ssa_ir.10 = uint64(%_pid) (u)/ uint64(%temp.ssa_ir.11);
|
|
|
+ uint64 %n = uint64(%n) * uint64(%temp.ssa_ir.10);
|
|
|
+ br block#7;
|
|
|
+
|
|
|
+block#6 else:
|
|
|
+ uint64 %n = uint64(%n) (u)/ uint64(3);
|
|
|
+ br block#7;
|
|
|
+
|
|
|
+block#7 endif:
|
|
|
+ br block#3;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_nested_if_blocks() {
|
|
|
+ let cfg_no = 0;
|
|
|
+
|
|
|
+ // read the example.sol file
|
|
|
+ let src = r#"
|
|
|
+ contract test {
|
|
|
+ enum suit { club, diamonds, hearts, spades }
|
|
|
+ enum value { two, three, four, five, six, seven, eight, nine, ten, jack, queen, king, ace }
|
|
|
+ struct card {
|
|
|
+ value v;
|
|
|
+ suit s;
|
|
|
+ }
|
|
|
+ function score_card(card memory c) public pure returns (uint32 score) {
|
|
|
+ if (c.s == suit.hearts) {
|
|
|
+ if (c.v == value.ace) {
|
|
|
+ score = 14;
|
|
|
+ }
|
|
|
+ if (c.v == value.king) {
|
|
|
+ score = 13;
|
|
|
+ }
|
|
|
+ if (c.v == value.queen) {
|
|
|
+ score = 12;
|
|
|
+ }
|
|
|
+ if (c.v == value.jack) {
|
|
|
+ score = 11;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // all others score 0
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ cfg_no,
|
|
|
+ r#"public function sol#2 test::test::function::score_card__test.card (ptr<struct.0>) returns (uint32):
|
|
|
+block#0 entry:
|
|
|
+ ptr<struct.0> %c = ptr<struct.0>(arg#0);
|
|
|
+ uint32 %score = 0;
|
|
|
+ ptr<uint8> %temp.ssa_ir.4 = access ptr<struct.0>(%c) member 1;
|
|
|
+ uint8 %temp.ssa_ir.3 = *ptr<uint8>(%temp.ssa_ir.4);
|
|
|
+ bool %temp.ssa_ir.2 = uint8(%temp.ssa_ir.3) == uint8(2);
|
|
|
+ cbr bool(%temp.ssa_ir.2) block#1 else block#2;
|
|
|
+
|
|
|
+block#1 then:
|
|
|
+ ptr<uint8> %temp.ssa_ir.7 = access ptr<struct.0>(%c) member 0;
|
|
|
+ uint8 %temp.ssa_ir.6 = *ptr<uint8>(%temp.ssa_ir.7);
|
|
|
+ bool %temp.ssa_ir.5 = uint8(%temp.ssa_ir.6) == uint8(12);
|
|
|
+ cbr bool(%temp.ssa_ir.5) block#3 else block#4;
|
|
|
+
|
|
|
+block#2 endif:
|
|
|
+ return uint32(%score);
|
|
|
+
|
|
|
+block#3 then:
|
|
|
+ uint32 %score = 14;
|
|
|
+ br block#4;
|
|
|
+
|
|
|
+block#4 endif:
|
|
|
+ ptr<uint8> %temp.ssa_ir.10 = access ptr<struct.0>(%c) member 0;
|
|
|
+ uint8 %temp.ssa_ir.9 = *ptr<uint8>(%temp.ssa_ir.10);
|
|
|
+ bool %temp.ssa_ir.8 = uint8(%temp.ssa_ir.9) == uint8(11);
|
|
|
+ cbr bool(%temp.ssa_ir.8) block#5 else block#6;
|
|
|
+
|
|
|
+block#5 then:
|
|
|
+ uint32 %score = 13;
|
|
|
+ br block#6;
|
|
|
+
|
|
|
+block#6 endif:
|
|
|
+ ptr<uint8> %temp.ssa_ir.13 = access ptr<struct.0>(%c) member 0;
|
|
|
+ uint8 %temp.ssa_ir.12 = *ptr<uint8>(%temp.ssa_ir.13);
|
|
|
+ bool %temp.ssa_ir.11 = uint8(%temp.ssa_ir.12) == uint8(10);
|
|
|
+ cbr bool(%temp.ssa_ir.11) block#7 else block#8;
|
|
|
+
|
|
|
+block#7 then:
|
|
|
+ uint32 %score = 12;
|
|
|
+ br block#8;
|
|
|
+
|
|
|
+block#8 endif:
|
|
|
+ ptr<uint8> %temp.ssa_ir.16 = access ptr<struct.0>(%c) member 0;
|
|
|
+ uint8 %temp.ssa_ir.15 = *ptr<uint8>(%temp.ssa_ir.16);
|
|
|
+ bool %temp.ssa_ir.14 = uint8(%temp.ssa_ir.15) == uint8(9);
|
|
|
+ cbr bool(%temp.ssa_ir.14) block#9 else block#10;
|
|
|
+
|
|
|
+block#9 then:
|
|
|
+ uint32 %score = 11;
|
|
|
+ br block#10;
|
|
|
+
|
|
|
+block#10 endif:
|
|
|
+ br block#2;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_init_struct() {
|
|
|
+ let cfg_no = 0;
|
|
|
+
|
|
|
+ // read the example.sol file
|
|
|
+ let src = r#"
|
|
|
+ contract test {
|
|
|
+ enum suit { club, diamonds, hearts, spades }
|
|
|
+ enum value { two, three, four, five, six, seven, eight, nine, ten, jack, queen, king, ace }
|
|
|
+ struct card {
|
|
|
+ value v;
|
|
|
+ suit s;
|
|
|
+ }
|
|
|
+ function ace_of_spaces() public pure returns (card memory) {
|
|
|
+ return card({s: suit.spades, v: value.ace });
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ cfg_no,
|
|
|
+ r#"public function sol#2 test::test::function::ace_of_spaces () returns (ptr<struct.0>):
|
|
|
+block#0 entry:
|
|
|
+ ptr<struct.0> %temp.ssa_ir.1 = struct { uint8(12), uint8(3) };
|
|
|
+ return ptr<struct.0>(%temp.ssa_ir.1);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_account_access() {
|
|
|
+ let cfg_no = 0;
|
|
|
+
|
|
|
+ // read the example.sol file
|
|
|
+ let src = r#"
|
|
|
+contract Foo {
|
|
|
+ @account(oneAccount)
|
|
|
+ @signer(mySigner)
|
|
|
+ @mutableAccount(otherAccount)
|
|
|
+ @mutableSigner(otherSigner)
|
|
|
+ function bar() external returns (uint64) {
|
|
|
+ assert(tx.accounts.mySigner.is_signer);
|
|
|
+ assert(tx.accounts.otherSigner.is_signer);
|
|
|
+ assert(tx.accounts.otherSigner.is_writable);
|
|
|
+ assert(tx.accounts.otherAccount.is_writable);
|
|
|
+
|
|
|
+ tx.accounts.otherAccount.data[0] = 0xca;
|
|
|
+ tx.accounts.otherSigner.data[1] = 0xfe;
|
|
|
+
|
|
|
+ return tx.accounts.oneAccount.lamports;
|
|
|
+ }
|
|
|
+}"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ cfg_no,
|
|
|
+ r#"public function sol#2 Foo::Foo::function::bar () returns (uint64):
|
|
|
+block#0 entry:
|
|
|
+ ptr<struct.SolAccountInfo[]> %temp.ssa_ir.12 = builtin: Accounts();
|
|
|
+ ptr<struct.SolAccountInfo> %temp.1 = ptr<struct.SolAccountInfo[]>(%temp.ssa_ir.12)[uint32(1)];
|
|
|
+ ptr<bool> %temp.ssa_ir.14 = access ptr<struct.SolAccountInfo>(%temp.1) member 5;
|
|
|
+ bool %temp.ssa_ir.13 = *ptr<bool>(%temp.ssa_ir.14);
|
|
|
+ cbr bool(%temp.ssa_ir.13) block#1 else block#2;
|
|
|
+
|
|
|
+block#1 noassert:
|
|
|
+ ptr<struct.SolAccountInfo[]> %temp.ssa_ir.15 = builtin: Accounts();
|
|
|
+ ptr<struct.SolAccountInfo> %temp.2 = ptr<struct.SolAccountInfo[]>(%temp.ssa_ir.15)[uint32(3)];
|
|
|
+ ptr<bool> %temp.ssa_ir.17 = access ptr<struct.SolAccountInfo>(%temp.2) member 5;
|
|
|
+ bool %temp.ssa_ir.16 = *ptr<bool>(%temp.ssa_ir.17);
|
|
|
+ cbr bool(%temp.ssa_ir.16) block#3 else block#4;
|
|
|
+
|
|
|
+block#2 doassert:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#3 noassert:
|
|
|
+ ptr<struct.SolAccountInfo[]> %temp.ssa_ir.18 = builtin: Accounts();
|
|
|
+ ptr<struct.SolAccountInfo> %temp.3 = ptr<struct.SolAccountInfo[]>(%temp.ssa_ir.18)[uint32(3)];
|
|
|
+ ptr<bool> %temp.ssa_ir.20 = access ptr<struct.SolAccountInfo>(%temp.3) member 6;
|
|
|
+ bool %temp.ssa_ir.19 = *ptr<bool>(%temp.ssa_ir.20);
|
|
|
+ cbr bool(%temp.ssa_ir.19) block#5 else block#6;
|
|
|
+
|
|
|
+block#4 doassert:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#5 noassert:
|
|
|
+ ptr<struct.SolAccountInfo[]> %temp.ssa_ir.21 = builtin: Accounts();
|
|
|
+ ptr<struct.SolAccountInfo> %temp.4 = ptr<struct.SolAccountInfo[]>(%temp.ssa_ir.21)[uint32(2)];
|
|
|
+ ptr<bool> %temp.ssa_ir.23 = access ptr<struct.SolAccountInfo>(%temp.4) member 6;
|
|
|
+ bool %temp.ssa_ir.22 = *ptr<bool>(%temp.ssa_ir.23);
|
|
|
+ cbr bool(%temp.ssa_ir.22) block#7 else block#8;
|
|
|
+
|
|
|
+block#6 doassert:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#7 noassert:
|
|
|
+ ptr<struct.SolAccountInfo[]> %temp.ssa_ir.24 = builtin: Accounts();
|
|
|
+ ptr<struct.SolAccountInfo> %temp.6 = ptr<struct.SolAccountInfo[]>(%temp.ssa_ir.24)[uint32(2)];
|
|
|
+ uint32 %index.temp.7 = 0;
|
|
|
+ ptr<slice<bytes1>> %temp.ssa_ir.28 = access ptr<struct.SolAccountInfo>(%temp.6) member 2;
|
|
|
+ ptr<slice<bytes1>> %temp.ssa_ir.27 = *ptr<slice<bytes1>>(%temp.ssa_ir.28);
|
|
|
+ uint32 %temp.ssa_ir.26 = builtin: ArrayLength(ptr<slice<bytes1>>(%temp.ssa_ir.27));
|
|
|
+ bool %temp.ssa_ir.25 = uint32(0) (u)>= uint32(%temp.ssa_ir.26);
|
|
|
+ cbr bool(%temp.ssa_ir.25) block#9 else block#10;
|
|
|
+
|
|
|
+block#8 doassert:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#9 out_of_bounds:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#10 in_bounds:
|
|
|
+ bytes1 %temp.5 = 202;
|
|
|
+ ptr<slice<bytes1>> %temp.ssa_ir.31 = access ptr<struct.SolAccountInfo>(%temp.6) member 2;
|
|
|
+ ptr<slice<bytes1>> %temp.ssa_ir.30 = *ptr<slice<bytes1>>(%temp.ssa_ir.31);
|
|
|
+ ptr<bytes1> %temp.ssa_ir.29 = ptr<slice<bytes1>>(%temp.ssa_ir.30)[uint32(%index.temp.7)];
|
|
|
+ store bytes1(202) to ptr<bytes1>(%temp.ssa_ir.29);
|
|
|
+ ptr<struct.SolAccountInfo[]> %temp.ssa_ir.32 = builtin: Accounts();
|
|
|
+ ptr<struct.SolAccountInfo> %temp.9 = ptr<struct.SolAccountInfo[]>(%temp.ssa_ir.32)[uint32(3)];
|
|
|
+ uint32 %index.temp.10 = 1;
|
|
|
+ ptr<slice<bytes1>> %temp.ssa_ir.36 = access ptr<struct.SolAccountInfo>(%temp.9) member 2;
|
|
|
+ ptr<slice<bytes1>> %temp.ssa_ir.35 = *ptr<slice<bytes1>>(%temp.ssa_ir.36);
|
|
|
+ uint32 %temp.ssa_ir.34 = builtin: ArrayLength(ptr<slice<bytes1>>(%temp.ssa_ir.35));
|
|
|
+ bool %temp.ssa_ir.33 = uint32(1) (u)>= uint32(%temp.ssa_ir.34);
|
|
|
+ cbr bool(%temp.ssa_ir.33) block#11 else block#12;
|
|
|
+
|
|
|
+block#11 out_of_bounds:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#12 in_bounds:
|
|
|
+ bytes1 %temp.8 = 254;
|
|
|
+ ptr<slice<bytes1>> %temp.ssa_ir.39 = access ptr<struct.SolAccountInfo>(%temp.9) member 2;
|
|
|
+ ptr<slice<bytes1>> %temp.ssa_ir.38 = *ptr<slice<bytes1>>(%temp.ssa_ir.39);
|
|
|
+ ptr<bytes1> %temp.ssa_ir.37 = ptr<slice<bytes1>>(%temp.ssa_ir.38)[uint32(%index.temp.10)];
|
|
|
+ store bytes1(254) to ptr<bytes1>(%temp.ssa_ir.37);
|
|
|
+ ptr<struct.SolAccountInfo[]> %temp.ssa_ir.40 = builtin: Accounts();
|
|
|
+ ptr<struct.SolAccountInfo> %temp.11 = ptr<struct.SolAccountInfo[]>(%temp.ssa_ir.40)[uint32(0)];
|
|
|
+ ptr<ptr<uint64>> %temp.ssa_ir.43 = access ptr<struct.SolAccountInfo>(%temp.11) member 1;
|
|
|
+ ptr<uint64> %temp.ssa_ir.42 = *ptr<ptr<uint64>>(%temp.ssa_ir.43);
|
|
|
+ uint64 %temp.ssa_ir.41 = *ptr<uint64>(%temp.ssa_ir.42);
|
|
|
+ return uint64(%temp.ssa_ir.41);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_assertion_using_require() {
|
|
|
+ let src = r#"contract Test {
|
|
|
+ function test(int32 num) public {
|
|
|
+ require(num > 10, "sesa");
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_polkadot_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#3 Test::Test::function::test__int32 (int32):
|
|
|
+block#0 entry:
|
|
|
+ int32 %num = int32(arg#0);
|
|
|
+ bool %temp.ssa_ir.1 = int32(%num) > int32(10);
|
|
|
+ cbr bool(%temp.ssa_ir.1) block#1 else block#2;
|
|
|
+
|
|
|
+block#1 noassert:
|
|
|
+ return;
|
|
|
+
|
|
|
+block#2 doassert:
|
|
|
+ ptr<slice<bytes1>> %temp.ssa_ir.2 = alloc ptr<slice<bytes1>>[uint32(9)] {08, c3, 79, a0, 10, 73, 65, 73, 61};
|
|
|
+ assert_failure ptr<slice<bytes1>>(%temp.ssa_ir.2);"#,
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_call_1() {
|
|
|
+ let src = r#"contract Test {
|
|
|
+ function test(int32 num) public {
|
|
|
+ check(num);
|
|
|
+ }
|
|
|
+
|
|
|
+ function check(int32 num) pure internal {
|
|
|
+ require(num > 10, "sesa");
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_polkadot_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#3 Test::Test::function::test__int32 (int32):
|
|
|
+block#0 entry:
|
|
|
+ int32 %num = int32(arg#0);
|
|
|
+ = call function#1(int32(%num));
|
|
|
+ return;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_return_data_and_return_code() {
|
|
|
+ let src = r#"contract Test {
|
|
|
+ function test() public {
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_lir_str_eq_by_name(
|
|
|
+ src,
|
|
|
+ "polkadot_call_dispatch",
|
|
|
+ r#"private function none polkadot_call_dispatch (ptr<uint8>, uint32, uint128, ptr<uint32>):
|
|
|
+block#0 entry:
|
|
|
+ uint32 %input_len.temp.4 = uint32(arg#1);
|
|
|
+ uint128 %value.temp.5 = uint128(arg#2);
|
|
|
+ ptr<uint8> %input_ptr.temp.6 = ptr<uint8>(arg#0);
|
|
|
+ bool %temp.ssa_ir.8 = uint32(%input_len.temp.4) (u)< uint32(4);
|
|
|
+ cbr bool(%temp.ssa_ir.8) block#2 else block#1;
|
|
|
+
|
|
|
+block#1 start_dispatch:
|
|
|
+ uint32 %selector.temp.7 = builtin: ReadFromBuffer(ptr<uint8>(%input_ptr.temp.6), uint32(0));
|
|
|
+ uint32 %temp.ssa_ir.9 = uint32(arg#3);
|
|
|
+ store uint32(%selector.temp.7) to uint32(%temp.ssa_ir.9);
|
|
|
+ switch uint32(%selector.temp.7):
|
|
|
+ case: uint32(1845340408) => block#3
|
|
|
+ default: block#2;
|
|
|
+
|
|
|
+block#2 fb_or_recv:
|
|
|
+ return_code "function selector invalid";
|
|
|
+
|
|
|
+block#3 func_0_dispatch:
|
|
|
+ bool %temp.ssa_ir.10 = uint128(%value.temp.5) (u)> uint128(0);
|
|
|
+ cbr bool(%temp.ssa_ir.10) block#4 else block#5;
|
|
|
+
|
|
|
+block#4 func_0_got_value:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#5 func_0_no_value:
|
|
|
+ = call function#0();
|
|
|
+ ptr<struct.vector<uint8>> %temp.ssa_ir.11 = alloc ptr<struct.vector<uint8>>[uint32(0)];
|
|
|
+ return_data ptr<struct.vector<uint8>>(%temp.ssa_ir.11) of length uint32(0);"#,
|
|
|
+ Target::default_polkadot(),
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_value_transfer() {
|
|
|
+ let src = r#"contract Test {
|
|
|
+ function transfer(address payable addr, uint128 amount) public {
|
|
|
+ addr.transfer(amount);
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ // Should be Polkadot
|
|
|
+ assert_polkadot_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#3 Test::Test::function::transfer__address_uint128 (uint8[32], uint128):
|
|
|
+block#0 entry:
|
|
|
+ uint8[32] %addr = uint8[32](arg#0);
|
|
|
+ uint128 %amount = uint128(arg#1);
|
|
|
+ uint128 %temp.ssa_ir.3 = (cast uint128(%amount) to uint128);
|
|
|
+ uint32 %success.temp.2 = value_transfer uint128(%temp.ssa_ir.3) to uint8[32](%addr);
|
|
|
+ bool %temp.ssa_ir.4 = uint32(%success.temp.2) == uint32(0);
|
|
|
+ cbr bool(%temp.ssa_ir.4) block#1 else block#2;
|
|
|
+
|
|
|
+block#1 transfer_success:
|
|
|
+ return;
|
|
|
+
|
|
|
+block#2 transfer_fail:
|
|
|
+ assert_failure;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_array_type_dynamic_storage() {
|
|
|
+ let cfg_no = 0;
|
|
|
+
|
|
|
+ // read the example.sol file
|
|
|
+ let src = r#"contract s {
|
|
|
+ int64[] a;
|
|
|
+
|
|
|
+ function test() public {
|
|
|
+ // push takes a single argument with the item to be added
|
|
|
+ a.push(128);
|
|
|
+ // push with no arguments adds 0
|
|
|
+ a.push();
|
|
|
+ // now we have two elements in our array, 128 and 0
|
|
|
+ assert(a.length == 2);
|
|
|
+ a[0] |= 64;
|
|
|
+ // pop removes the last element
|
|
|
+ a.pop();
|
|
|
+ // you can assign the return value of pop
|
|
|
+ int64 v = a.pop();
|
|
|
+ assert(v == 192);
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ cfg_no,
|
|
|
+ r#"public function sol#2 s::s::function::test ():
|
|
|
+block#0 entry:
|
|
|
+ int64 %temp.1 = push_storage uint32(16) int64(128);
|
|
|
+ int64 %temp.2 = push_storage uint32(16) int64(0);
|
|
|
+ uint32 %temp.ssa_ir.9 = storage_arr_len(uint32(16));
|
|
|
+ bool %temp.ssa_ir.8 = uint32(%temp.ssa_ir.9) == uint32(2);
|
|
|
+ cbr bool(%temp.ssa_ir.8) block#1 else block#2;
|
|
|
+
|
|
|
+block#1 noassert:
|
|
|
+ uint256 %index.temp.3 = 0;
|
|
|
+ uint32 %temp.ssa_ir.12 = storage_arr_len(uint32(16));
|
|
|
+ uint256 %temp.ssa_ir.11 = (zext uint32(%temp.ssa_ir.12) to uint256);
|
|
|
+ bool %temp.ssa_ir.10 = uint256(0) (u)>= uint256(%temp.ssa_ir.11);
|
|
|
+ cbr bool(%temp.ssa_ir.10) block#3 else block#4;
|
|
|
+
|
|
|
+block#2 doassert:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#3 out_of_bounds:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#4 in_bounds:
|
|
|
+ uint32 %temp.ssa_ir.14 = (trunc uint256(%index.temp.3) to uint32);
|
|
|
+ storage_ptr<int64> %temp.ssa_ir.13 = uint32(16)[uint32(%temp.ssa_ir.14)];
|
|
|
+ int64 %temp.4 = load_storage storage_ptr<int64>(%temp.ssa_ir.13);
|
|
|
+ uint256 %index.temp.6 = 0;
|
|
|
+ uint32 %temp.ssa_ir.17 = storage_arr_len(uint32(16));
|
|
|
+ uint256 %temp.ssa_ir.16 = (zext uint32(%temp.ssa_ir.17) to uint256);
|
|
|
+ bool %temp.ssa_ir.15 = uint256(0) (u)>= uint256(%temp.ssa_ir.16);
|
|
|
+ cbr bool(%temp.ssa_ir.15) block#5 else block#6;
|
|
|
+
|
|
|
+block#5 out_of_bounds:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#6 in_bounds:
|
|
|
+ int64 %temp.5 = int64(%temp.4) | int64(64);
|
|
|
+ uint32 %temp.ssa_ir.19 = (trunc uint256(%index.temp.6) to uint32);
|
|
|
+ storage_ptr<int64> %temp.ssa_ir.18 = uint32(16)[uint32(%temp.ssa_ir.19)];
|
|
|
+ set_storage storage_ptr<int64>(%temp.ssa_ir.18) int64(%temp.5);
|
|
|
+ pop_storage uint32(16);
|
|
|
+ int64 %temp.7 = pop_storage uint32(16);
|
|
|
+ int64 %v = int64(%temp.7);
|
|
|
+ bool %temp.ssa_ir.20 = int64(%v) == int64(192);
|
|
|
+ cbr bool(%temp.ssa_ir.20) block#7 else block#8;
|
|
|
+
|
|
|
+block#7 noassert:
|
|
|
+ return;
|
|
|
+
|
|
|
+block#8 doassert:
|
|
|
+ assert_failure;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_switch() {
|
|
|
+ let src = r#"contract foo {
|
|
|
+ function test(uint x) public pure {
|
|
|
+ uint256 yy=0;
|
|
|
+ assembly {
|
|
|
+ let y := 5
|
|
|
+ switch and(x, 3)
|
|
|
+ case 0 {
|
|
|
+ y := 5
|
|
|
+ x := 5
|
|
|
+ }
|
|
|
+ case 1 {
|
|
|
+ y := 7
|
|
|
+ x := 9
|
|
|
+ }
|
|
|
+ case 3 {
|
|
|
+ y := 10
|
|
|
+ x := 80
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 foo::foo::function::test__uint256 (uint256):
|
|
|
+block#0 entry:
|
|
|
+ uint256 %x = uint256(arg#0);
|
|
|
+ uint256 %y = 5;
|
|
|
+ uint256 %temp.ssa_ir.3 = uint256(%x) & uint256(3);
|
|
|
+ switch uint256(%temp.ssa_ir.3):
|
|
|
+ case: uint256(0) => block#2,
|
|
|
+ case: uint256(1) => block#3,
|
|
|
+ case: uint256(3) => block#4
|
|
|
+ default: block#1;
|
|
|
+
|
|
|
+block#1 end_switch:
|
|
|
+ return;
|
|
|
+
|
|
|
+block#2 case_0:
|
|
|
+ uint256 %y = 5;
|
|
|
+ uint256 %x = 5;
|
|
|
+ br block#1;
|
|
|
+
|
|
|
+block#3 case_1:
|
|
|
+ uint256 %y = 7;
|
|
|
+ uint256 %x = 9;
|
|
|
+ br block#1;
|
|
|
+
|
|
|
+block#4 case_2:
|
|
|
+ uint256 %y = 10;
|
|
|
+ uint256 %x = 80;
|
|
|
+ br block#1;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_keccak256() {
|
|
|
+ let src = r#"contract b {
|
|
|
+ struct user {
|
|
|
+ bool exists;
|
|
|
+ address addr;
|
|
|
+ }
|
|
|
+ mapping(string => user) users;
|
|
|
+ function add(string name, address addr) public {
|
|
|
+ // This construction is not recommended, because it requires two hash calculations.
|
|
|
+ // See the tip below.
|
|
|
+ users[name].exists = true;
|
|
|
+ users[name].addr = addr;
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_polkadot_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#3 b::b::function::add__string_address (ptr<struct.vector<uint8>>, uint8[32]):
|
|
|
+block#0 entry:
|
|
|
+ ptr<struct.vector<uint8>> %name = ptr<struct.vector<uint8>>(arg#0);
|
|
|
+ uint8[32] %addr = uint8[32](arg#1);
|
|
|
+ bool %temp.2 = true;
|
|
|
+ storage_ptr<mapping(struct.vector<uint8> => struct.0)> %temp.ssa_ir.5 = keccak256(uint256(0), ptr<struct.vector<uint8>>(%name));
|
|
|
+ uint256 %temp.ssa_ir.4 = storage_ptr<mapping(struct.vector<uint8> => struct.0)>(%temp.ssa_ir.5) (of)+ uint256(0);
|
|
|
+ set_storage uint256(%temp.ssa_ir.4) true;
|
|
|
+ uint8[32] %temp.3 = uint8[32](arg#1);
|
|
|
+ storage_ptr<mapping(struct.vector<uint8> => struct.0)> %temp.ssa_ir.7 = keccak256(uint256(0), ptr<struct.vector<uint8>>(%name));
|
|
|
+ uint256 %temp.ssa_ir.6 = storage_ptr<mapping(struct.vector<uint8> => struct.0)>(%temp.ssa_ir.7) (of)+ uint256(1);
|
|
|
+ set_storage uint256(%temp.ssa_ir.6) uint8[32](%temp.3);
|
|
|
+ return;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_internal_function_cfg() {
|
|
|
+ let src = r#"contract A {
|
|
|
+ function foo(uint a) internal returns (uint) {
|
|
|
+ return a+2;
|
|
|
+ }
|
|
|
+
|
|
|
+ function bar(uint b) public returns (uint) {
|
|
|
+ function (uint) returns (uint) fPtr = foo;
|
|
|
+ return fPtr(b);
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_polkadot_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 1,
|
|
|
+ r#"public function sol#4 A::A::function::bar__uint256 (uint256) returns (uint256):
|
|
|
+block#0 entry:
|
|
|
+ uint256 %b = uint256(arg#0);
|
|
|
+ ptr<function (uint256) returns (uint256)> %temp.ssa_ir.6 = function#0;
|
|
|
+ ptr<function (uint256) returns (uint256)> %fPtr = (cast ptr<function (uint256) returns (uint256)>(%temp.ssa_ir.6) to ptr<function (uint256) returns (uint256)>);
|
|
|
+ uint256 %.temp.5 = call ptr<function (uint256) returns (uint256)>(%fPtr)(uint256(%b));
|
|
|
+ return uint256(%.temp.5);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_sign_ext() {
|
|
|
+ let src = r#"contract Test {
|
|
|
+ function test(int32 a) public returns (int128) {
|
|
|
+ return int128(a);
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 Test::Test::function::test__int32 (int32) returns (int128):
|
|
|
+block#0 entry:
|
|
|
+ int32 %a = int32(arg#0);
|
|
|
+ int128 %temp.ssa_ir.2 = (sext int32(%a) to int128);
|
|
|
+ return int128(%temp.ssa_ir.2);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_string_compare() {
|
|
|
+ let src = r#"contract Test {
|
|
|
+ function test(string a, string b) public returns (bool) {
|
|
|
+ return a == b;
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 Test::Test::function::test__string_string (ptr<struct.vector<uint8>>, ptr<struct.vector<uint8>>) returns (bool):
|
|
|
+block#0 entry:
|
|
|
+ ptr<struct.vector<uint8>> %a = ptr<struct.vector<uint8>>(arg#0);
|
|
|
+ ptr<struct.vector<uint8>> %b = ptr<struct.vector<uint8>>(arg#1);
|
|
|
+ bool %temp.ssa_ir.3 = strcmp(ptr<struct.vector<uint8>>(%a), ptr<struct.vector<uint8>>(%b));
|
|
|
+ return bool(%temp.ssa_ir.3);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_const_array() {
|
|
|
+ let src = r#"contract Test {
|
|
|
+ uint32[5] constant arr = [1, 2, 3, 4, 5];
|
|
|
+ function test() public returns (uint32) {
|
|
|
+ return arr[0];
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 Test::Test::function::test () returns (uint32):
|
|
|
+block#0 entry:
|
|
|
+ uint32 %index.temp.1 = 0;
|
|
|
+ bool %temp.ssa_ir.2 = uint32(0) (u)>= uint32(5);
|
|
|
+ cbr bool(%temp.ssa_ir.2) block#1 else block#2;
|
|
|
+
|
|
|
+block#1 out_of_bounds:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#2 in_bounds:
|
|
|
+ ptr<uint32[5]> %temp.ssa_ir.5 = const ptr<uint32[5]> [uint32(1), uint32(2), uint32(3), uint32(4), uint32(5)];
|
|
|
+ ptr<uint32> %temp.ssa_ir.4 = ptr<uint32[5]>(%temp.ssa_ir.5)[uint32(0)];
|
|
|
+ uint32 %temp.ssa_ir.3 = *ptr<uint32>(%temp.ssa_ir.4);
|
|
|
+ return uint32(%temp.ssa_ir.3);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_account_meta() {
|
|
|
+ let src = r#"import 'solana';
|
|
|
+ contract creator {
|
|
|
+ @mutableSigner(data_account_to_initialize)
|
|
|
+ @mutableSigner(payer)
|
|
|
+ function create_with_metas() external {
|
|
|
+ AccountMeta[3] metas = [
|
|
|
+ AccountMeta({
|
|
|
+ pubkey: tx.accounts.data_account_to_initialize.key,
|
|
|
+ is_signer: true,
|
|
|
+ is_writable: true}),
|
|
|
+ AccountMeta({
|
|
|
+ pubkey: tx.accounts.payer.key,
|
|
|
+ is_signer: true,
|
|
|
+ is_writable: true}),
|
|
|
+ AccountMeta({
|
|
|
+ pubkey: address"11111111111111111111111111111111",
|
|
|
+ is_writable: false,
|
|
|
+ is_signer: false})
|
|
|
+ ];
|
|
|
+ Child.new{accounts: metas}();
|
|
|
+ Child.use_metas{accounts: []}();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ @program_id("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT")
|
|
|
+ contract Child {
|
|
|
+ @payer(payer)
|
|
|
+ constructor() {
|
|
|
+ print("In child constructor");
|
|
|
+ }
|
|
|
+ function use_metas() pure public {
|
|
|
+ print("I am using metas");
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 creator::creator::function::create_with_metas ():
|
|
|
+block#0 entry:
|
|
|
+ ptr<struct.SolAccountInfo[]> %temp.ssa_ir.14 = builtin: Accounts();
|
|
|
+ ptr<struct.SolAccountInfo> %temp.10 = ptr<struct.SolAccountInfo[]>(%temp.ssa_ir.14)[uint32(0)];
|
|
|
+ ptr<struct.SolAccountInfo[]> %temp.ssa_ir.15 = builtin: Accounts();
|
|
|
+ ptr<struct.SolAccountInfo> %temp.11 = ptr<struct.SolAccountInfo[]>(%temp.ssa_ir.15)[uint32(1)];
|
|
|
+ ptr<ptr<uint8[32]>> %temp.ssa_ir.18 = access ptr<struct.SolAccountInfo>(%temp.10) member 0;
|
|
|
+ ptr<uint8[32]> %temp.ssa_ir.17 = *ptr<ptr<uint8[32]>>(%temp.ssa_ir.18);
|
|
|
+ ptr<struct.SolAccountMeta> %temp.ssa_ir.16 = struct { ptr<uint8[32]>(%temp.ssa_ir.17), true, true };
|
|
|
+ ptr<ptr<uint8[32]>> %temp.ssa_ir.21 = access ptr<struct.SolAccountInfo>(%temp.11) member 0;
|
|
|
+ ptr<uint8[32]> %temp.ssa_ir.20 = *ptr<ptr<uint8[32]>>(%temp.ssa_ir.21);
|
|
|
+ ptr<struct.SolAccountMeta> %temp.ssa_ir.19 = struct { ptr<uint8[32]>(%temp.ssa_ir.20), true, true };
|
|
|
+ ptr<uint8[32]> %temp.ssa_ir.23 = &uint8[32](0);
|
|
|
+ ptr<struct.SolAccountMeta> %temp.ssa_ir.22 = struct { ptr<uint8[32]>(%temp.ssa_ir.23), false, false };
|
|
|
+ ptr<struct.SolAccountMeta[3]> %metas = ptr<struct.SolAccountMeta[3]> [ptr<struct.SolAccountMeta>(%temp.ssa_ir.16), ptr<struct.SolAccountMeta>(%temp.ssa_ir.19), ptr<struct.SolAccountMeta>(%temp.ssa_ir.22)];
|
|
|
+ ptr<struct.vector<uint8>> %abi_encoded.temp.12 = alloc ptr<struct.vector<uint8>>[uint32(8)];
|
|
|
+ bytes8 %temp.ssa_ir.24 = bytes8 hex"87_2c_cd_c6_19_01_48_bc";
|
|
|
+ write_buf ptr<struct.vector<uint8>>(%abi_encoded.temp.12) offset:uint32(0) value:bytes8(%temp.ssa_ir.24);
|
|
|
+ _ = call_ext [regular] address:uint8[32](78642644713358252795404932596995255556623171005675782810573618728006773308276) payload:ptr<struct.vector<uint8>>(%abi_encoded.temp.12) value:uint64(0) gas:uint64(0) accounts:ptr<struct.SolAccountMeta[3]>(%metas) seeds:_ contract_no:1, function_no:3 flags:_;
|
|
|
+ ptr<struct.vector<uint8>> %abi_encoded.temp.13 = alloc ptr<struct.vector<uint8>>[uint32(8)];
|
|
|
+ bytes8 %temp.ssa_ir.25 = bytes8 hex"97_f8_3c_a2_18_9f_26_9d";
|
|
|
+ write_buf ptr<struct.vector<uint8>>(%abi_encoded.temp.13) offset:uint32(0) value:bytes8(%temp.ssa_ir.25);
|
|
|
+ _ = call_ext [regular] address:uint8[32](78642644713358252795404932596995255556623171005675782810573618728006773308276) payload:ptr<struct.vector<uint8>>(%abi_encoded.temp.13) value:uint64(0) gas:uint64(0) accounts:none seeds:_ contract_no:1, function_no:4 flags:_;
|
|
|
+ return;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_constructor() {
|
|
|
+ let src = r#"
|
|
|
+ contract B {
|
|
|
+ A aa;
|
|
|
+ function test(uint a) public {
|
|
|
+ aa = new A(a);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ contract A {
|
|
|
+ uint public a;
|
|
|
+ constructor(uint b) {
|
|
|
+ a = b;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ "#;
|
|
|
+
|
|
|
+ assert_polkadot_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#3 B::B::function::test__uint256 (uint256):
|
|
|
+block#0 entry:
|
|
|
+ uint256 %a = uint256(arg#0);
|
|
|
+ ptr<struct.vector<uint8>> %abi_encoded.temp.18 = alloc ptr<struct.vector<uint8>>[uint32(36)];
|
|
|
+ uint32 %temp.ssa_ir.20 = uint32 hex"58_16_c4_25";
|
|
|
+ write_buf ptr<struct.vector<uint8>>(%abi_encoded.temp.18) offset:uint32(0) value:uint32(%temp.ssa_ir.20);
|
|
|
+ write_buf ptr<struct.vector<uint8>>(%abi_encoded.temp.18) offset:uint32(4) value:uint256(%a);
|
|
|
+ uint32 %success.temp.17, uint8[32] %temp.16 = constructor(no: 5, contract_no:1) salt:_ value:_ gas:uint64(0) address:_ seeds:_ encoded-buffer:ptr<struct.vector<uint8>>(%abi_encoded.temp.18) accounts:absent
|
|
|
+ switch uint32(%success.temp.17):
|
|
|
+ case: uint32(0) => block#1,
|
|
|
+ case: uint32(2) => block#2
|
|
|
+ default: block#3;
|
|
|
+
|
|
|
+block#1 ret_success:
|
|
|
+ uint8[32] %temp.19 = uint8[32](%temp.16);
|
|
|
+ set_storage uint256(0) uint8[32](%temp.19);
|
|
|
+ return;
|
|
|
+
|
|
|
+block#2 ret_bubble:
|
|
|
+ ptr<struct.vector<uint8>> %temp.ssa_ir.21 = (extern_call_ret_data);
|
|
|
+ assert_failure ptr<struct.vector<uint8>>(%temp.ssa_ir.21);
|
|
|
+
|
|
|
+block#3 ret_no_data:
|
|
|
+ assert_failure;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_external_fn() {
|
|
|
+ let src = r#"contract Testing {
|
|
|
+ function testExternalFunction(
|
|
|
+ bytes memory buffer
|
|
|
+ ) public view returns (bytes8, address) {
|
|
|
+ function(uint8) external returns (int8) fPtr = abi.decode(
|
|
|
+ buffer,
|
|
|
+ (function(uint8) external returns (int8))
|
|
|
+ );
|
|
|
+ return (fPtr.selector, fPtr.address);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ "#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 Testing::Testing::function::testExternalFunction__bytes (ptr<struct.vector<uint8>>) returns (bytes8, uint8[32]):
|
|
|
+block#0 entry:
|
|
|
+ ptr<struct.vector<uint8>> %buffer = ptr<struct.vector<uint8>>(arg#0);
|
|
|
+ uint32 %temp.4 = builtin: ArrayLength(ptr<struct.vector<uint8>>(%buffer));
|
|
|
+ bool %temp.ssa_ir.5 = uint32(40) (u)< uint32(%temp.4);
|
|
|
+ cbr bool(%temp.ssa_ir.5) block#1 else block#2;
|
|
|
+
|
|
|
+block#1 inbounds:
|
|
|
+ bool %temp.ssa_ir.6 = uint32(40) (u)< uint32(%temp.4);
|
|
|
+ cbr bool(%temp.ssa_ir.6) block#3 else block#4;
|
|
|
+
|
|
|
+block#2 out_of_bounds:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#3 not_all_bytes_read:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#4 buffer_read:
|
|
|
+ uint64 %temp.ssa_ir.9 = builtin: ReadFromBuffer(ptr<struct.vector<uint8>>(%buffer), uint32(0));
|
|
|
+ uint8[32] %temp.ssa_ir.10 = builtin: ReadFromBuffer(ptr<struct.vector<uint8>>(%buffer), uint32(8));
|
|
|
+ ptr<struct.ExternalFunction> %temp.ssa_ir.8 = struct { uint64(%temp.ssa_ir.9), uint8[32](%temp.ssa_ir.10) };
|
|
|
+ ptr<struct.ExternalFunction> %temp.ssa_ir.7 = (cast ptr<struct.ExternalFunction>(%temp.ssa_ir.8) to ptr<struct.ExternalFunction>);
|
|
|
+ ptr<struct.ExternalFunction> %fPtr = (cast ptr<struct.ExternalFunction>(%temp.ssa_ir.7) to ptr<struct.ExternalFunction>);
|
|
|
+ ptr<uint64> %temp.ssa_ir.13 = access ptr<struct.ExternalFunction>(%fPtr) member 0;
|
|
|
+ uint64 %temp.ssa_ir.12 = *ptr<uint64>(%temp.ssa_ir.13);
|
|
|
+ bytes8 %temp.ssa_ir.11 = (cast uint64(%temp.ssa_ir.12) to bytes8);
|
|
|
+ ptr<uint8[32]> %temp.ssa_ir.15 = access ptr<struct.ExternalFunction>(%fPtr) member 1;
|
|
|
+ uint8[32] %temp.ssa_ir.14 = *ptr<uint8[32]>(%temp.ssa_ir.15);
|
|
|
+ return bytes8(%temp.ssa_ir.11), uint8[32](%temp.ssa_ir.14);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_push_pop_mem() {
|
|
|
+ let src = r#"
|
|
|
+ contract foo {
|
|
|
+ struct s {
|
|
|
+ int32 f1;
|
|
|
+ bool f2;
|
|
|
+ }
|
|
|
+ function test() public {
|
|
|
+ s[] bar = new s[](0);
|
|
|
+ s memory n = bar.push();
|
|
|
+ bar.pop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ "#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 foo::foo::function::test ():
|
|
|
+block#0 entry:
|
|
|
+ uint32 %array_length.temp.2 = 0;
|
|
|
+ ptr<struct.0[]> %bar = alloc ptr<struct.0[]>[uint32(0)];
|
|
|
+ ptr<struct.0> %temp.ssa_ir.5 = struct { };
|
|
|
+ ptr<struct.0> %temp.3 = push_mem ptr<struct.0[]>(%bar) ptr<struct.0>(%temp.ssa_ir.5);
|
|
|
+ uint32 %array_length.temp.2 = 1;
|
|
|
+ ptr<struct.0> %temp.4 = pop_mem ptr<struct.0[]>(%bar);
|
|
|
+ uint32 %array_length.temp.2 = 0;
|
|
|
+ return;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_math() {
|
|
|
+ let src = r#"contract store {
|
|
|
+ enum enum_bar { bar1, bar2, bar3, bar4 }
|
|
|
+ uint64 u64;
|
|
|
+ uint32 u32;
|
|
|
+ int16 i16;
|
|
|
+ int256 i256;
|
|
|
+ uint256 u256;
|
|
|
+ string str;
|
|
|
+ bytes bs = hex"b00b1e";
|
|
|
+ bytes4 fixedbytes;
|
|
|
+ enum_bar bar;
|
|
|
+ function do_ops() public {
|
|
|
+ unchecked {
|
|
|
+ // u64 will overflow to 1
|
|
|
+ u64 += 2;
|
|
|
+ u32 &= 0xffff;
|
|
|
+ // another overflow
|
|
|
+ i16 += 1;
|
|
|
+ i256 ^= 1;
|
|
|
+ u256 *= 600;
|
|
|
+ str = "";
|
|
|
+ bs[1] = 0xff;
|
|
|
+ // make upper case
|
|
|
+ fixedbytes |= 0x20202020;
|
|
|
+ bar = enum_bar.bar4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 store::store::function::do_ops ():
|
|
|
+block#0 entry:
|
|
|
+ uint64 %temp.0 = load_storage uint32(16);
|
|
|
+ uint64 %temp.1 = uint64(%temp.0) (of)+ uint64(2);
|
|
|
+ set_storage uint32(16) uint64(%temp.1);
|
|
|
+ uint32 %temp.2 = load_storage uint32(24);
|
|
|
+ uint32 %temp.3 = uint32(%temp.2) & uint32(65535);
|
|
|
+ set_storage uint32(24) uint32(%temp.3);
|
|
|
+ int16 %temp.4 = load_storage uint32(28);
|
|
|
+ int16 %temp.5 = int16(%temp.4) (of)+ int16(1);
|
|
|
+ set_storage uint32(28) int16(%temp.5);
|
|
|
+ int256 %temp.6 = load_storage uint32(32);
|
|
|
+ int256 %temp.7 = int256(%temp.6) ^ int256(1);
|
|
|
+ set_storage uint32(32) int256(%temp.7);
|
|
|
+ uint256 %temp.8 = load_storage uint32(64);
|
|
|
+ uint64 %temp.ssa_ir.16 = (trunc uint256(%temp.8) to uint64);
|
|
|
+ uint64 %temp.ssa_ir.15 = uint64(%temp.ssa_ir.16) (of)* uint64(600);
|
|
|
+ uint256 %temp.9 = (zext uint64(%temp.ssa_ir.15) to uint256);
|
|
|
+ set_storage uint32(64) uint256(%temp.9);
|
|
|
+ ptr<struct.vector<uint8>> %temp.10 = alloc ptr<slice<bytes1>>[uint32(0)] {};
|
|
|
+ set_storage uint32(96) ptr<struct.vector<uint8>>(%temp.10);
|
|
|
+ bytes1 %temp.11 = 255;
|
|
|
+ set_storage_bytes uint32(100) offset:uint32(1) value:bytes1(255);
|
|
|
+ bytes4 %temp.12 = load_storage uint32(104);
|
|
|
+ bytes4 %temp.13 = bytes4(%temp.12) | bytes4(538976288);
|
|
|
+ set_storage uint32(104) bytes4(%temp.13);
|
|
|
+ uint8 %temp.14 = 3;
|
|
|
+ set_storage uint32(108) uint8(3);
|
|
|
+ return;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_byte_cast() {
|
|
|
+ let src = r#"contract Cast {
|
|
|
+ function test(uint256 num) public returns (bytes) {
|
|
|
+ bytes smol_buf = new bytes(num);
|
|
|
+ bytes32 b32 = bytes32(smol_buf);
|
|
|
+ return b32;
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 Cast::Cast::function::test__uint256 (uint256) returns (ptr<struct.vector<uint8>>):
|
|
|
+block#0 entry:
|
|
|
+ uint256 %num = uint256(arg#0);
|
|
|
+ uint256 %value.temp.4 = uint256(arg#0);
|
|
|
+ bool %temp.ssa_ir.5 = uint256(%value.temp.4) (u)>= uint256(4294967296);
|
|
|
+ cbr bool(%temp.ssa_ir.5) block#1 else block#2;
|
|
|
+
|
|
|
+block#1 out_of_bounds:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#2 in_bounds:
|
|
|
+ uint32 %temp.ssa_ir.6 = (trunc uint256(%value.temp.4) to uint32);
|
|
|
+ ptr<struct.vector<uint8>> %smol_buf = alloc ptr<struct.vector<uint8>>[uint32(%temp.ssa_ir.6)];
|
|
|
+ bytes32 %b32 = (cast ptr<struct.vector<uint8>>(%smol_buf) to bytes32);
|
|
|
+ ptr<struct.vector<uint8>> %temp.ssa_ir.7 = (cast bytes32(%b32) to ptr<struct.vector<uint8>>);
|
|
|
+ return ptr<struct.vector<uint8>>(%temp.ssa_ir.7);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_signed_modulo() {
|
|
|
+ let src = r#"contract SignedModulo {
|
|
|
+ function test(int256 a, int256 b) public returns (int256) {
|
|
|
+ int256 c = a % b;
|
|
|
+ return c;
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 SignedModulo::SignedModulo::function::test__int256_int256 (int256, int256) returns (int256):
|
|
|
+block#0 entry:
|
|
|
+ int256 %a = int256(arg#0);
|
|
|
+ int256 %b = int256(arg#1);
|
|
|
+ int256 %c = int256(%a) % int256(%b);
|
|
|
+ return int256(%c);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_compare() {
|
|
|
+ let src = r#"contract example {
|
|
|
+ int16 stored;
|
|
|
+
|
|
|
+ function func(int256 x) public {
|
|
|
+ if (x < type(int16).min || x > type(int16).max) {
|
|
|
+ revert("value will not fit");
|
|
|
+ }
|
|
|
+
|
|
|
+ stored = int16(x);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ "#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 example::example::function::func__int256 (int256):
|
|
|
+block#0 entry:
|
|
|
+ int256 %x = int256(arg#0);
|
|
|
+ bool %or.temp.1 = true;
|
|
|
+ bool %temp.ssa_ir.3 = int256(%x) < int256(-32768);
|
|
|
+ cbr bool(%temp.ssa_ir.3) block#2 else block#1;
|
|
|
+
|
|
|
+block#1 or_right_side:
|
|
|
+ bool %or.temp.1 = int256(%x) > int256(32767);
|
|
|
+ br block#2;
|
|
|
+
|
|
|
+block#2 or_end:
|
|
|
+ cbr bool(%or.temp.1) block#3 else block#4;
|
|
|
+
|
|
|
+block#3 then:
|
|
|
+ assert_failure;
|
|
|
+
|
|
|
+block#4 endif:
|
|
|
+ int16 %temp.2 = (trunc int256(%x) to int16);
|
|
|
+ set_storage uint32(16) int16(%temp.2);
|
|
|
+ return;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_array() {
|
|
|
+ let src = r#"
|
|
|
+ contract C {
|
|
|
+ function testVec() public pure returns (uint32) {
|
|
|
+ uint32[3] vec = [1, 2, 3];
|
|
|
+ return vec.length;
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 C::C::function::testVec () returns (uint32):
|
|
|
+block#0 entry:
|
|
|
+ ptr<uint32[3]> %vec = ptr<uint32[3]> [uint32(1), uint32(2), uint32(3)];
|
|
|
+ return uint32(3);"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_clear_storage() {
|
|
|
+ let src = r#"
|
|
|
+ struct S {
|
|
|
+ function() e;
|
|
|
+ }
|
|
|
+
|
|
|
+ contract C {
|
|
|
+ S s;
|
|
|
+ function test() public {
|
|
|
+ delete s.e;
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 C::C::function::test ():
|
|
|
+block#0 entry:
|
|
|
+ clear_storage uint32(64);
|
|
|
+ return;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_call_ext() {
|
|
|
+ let src = r#"
|
|
|
+ contract adult {
|
|
|
+ function test(address id) external {
|
|
|
+ hatchling.new{program_id: id}("luna");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ contract hatchling {
|
|
|
+ string name;
|
|
|
+ constructor(string id) payable {
|
|
|
+ name = id;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ "#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 adult::adult::function::test__address (uint8[32]):
|
|
|
+block#0 entry:
|
|
|
+ uint8[32] %id = uint8[32](arg#0);
|
|
|
+ ptr<struct.vector<uint8>> %temp.ssa_ir.15 = alloc ptr<struct.vector<uint8>>[uint32(4)] {6c, 75, 6e, 61};
|
|
|
+ uint32 %temp.ssa_ir.14 = builtin: ArrayLength(ptr<struct.vector<uint8>>(%temp.ssa_ir.15));
|
|
|
+ uint32 %temp.ssa_ir.13 = uint32(%temp.ssa_ir.14) + uint32(4);
|
|
|
+ uint32 %temp.ssa_ir.12 = uint32(8) + uint32(%temp.ssa_ir.13);
|
|
|
+ ptr<struct.vector<uint8>> %abi_encoded.temp.10 = alloc ptr<struct.vector<uint8>>[uint32(%temp.ssa_ir.12)];
|
|
|
+ bytes8 %temp.ssa_ir.16 = bytes8 hex"87_2c_cd_c6_19_01_48_bc";
|
|
|
+ write_buf ptr<struct.vector<uint8>>(%abi_encoded.temp.10) offset:uint32(0) value:bytes8(%temp.ssa_ir.16);
|
|
|
+ ptr<struct.vector<uint8>> %temp.ssa_ir.17 = alloc ptr<struct.vector<uint8>>[uint32(4)] {6c, 75, 6e, 61};
|
|
|
+ uint32 %temp.11 = builtin: ArrayLength(ptr<struct.vector<uint8>>(%temp.ssa_ir.17));
|
|
|
+ write_buf ptr<struct.vector<uint8>>(%abi_encoded.temp.10) offset:uint32(8) value:uint32(%temp.11);
|
|
|
+ ptr<struct.vector<uint8>> %temp.ssa_ir.18 = alloc ptr<struct.vector<uint8>>[uint32(4)] {6c, 75, 6e, 61};
|
|
|
+ ptr<uint8> %temp.ssa_ir.19 = ptr_add(ptr<struct.vector<uint8>>(%abi_encoded.temp.10), uint32(12));
|
|
|
+ memcopy ptr<struct.vector<uint8>>(%temp.ssa_ir.18) to ptr<uint8>(%temp.ssa_ir.19) for uint32(%temp.11) bytes;
|
|
|
+ ptr<struct.SolAccountInfo[]> %temp.ssa_ir.25 = builtin: Accounts();
|
|
|
+ ptr<struct.SolAccountInfo> %temp.ssa_ir.24 = ptr<struct.SolAccountInfo[]>(%temp.ssa_ir.25)[uint32(1)];
|
|
|
+ ptr<ptr<uint8[32]>> %temp.ssa_ir.23 = access ptr<struct.SolAccountInfo>(%temp.ssa_ir.24) member 0;
|
|
|
+ ptr<uint8[32]> %temp.ssa_ir.22 = *ptr<ptr<uint8[32]>>(%temp.ssa_ir.23);
|
|
|
+ ptr<struct.SolAccountMeta> %temp.ssa_ir.21 = struct { ptr<uint8[32]>(%temp.ssa_ir.22), true, false };
|
|
|
+ ptr<struct.SolAccountMeta[1]> %temp.ssa_ir.20 = ptr<struct.SolAccountMeta[1]> [ptr<struct.SolAccountMeta>(%temp.ssa_ir.21)];
|
|
|
+ _ = call_ext [regular] address:uint8[32](%id) payload:ptr<struct.vector<uint8>>(%abi_encoded.temp.10) value:uint64(0) gas:uint64(0) accounts:ptr<struct.SolAccountMeta[1]>(%temp.ssa_ir.20) seeds:_ contract_no:1, function_no:3 flags:_;
|
|
|
+ return;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_emit_event() {
|
|
|
+ let src = r#"
|
|
|
+ contract mytokenEvent {
|
|
|
+ event Debugging(int b);
|
|
|
+
|
|
|
+ function test() public {
|
|
|
+ emit Debugging(1);
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 mytokenEvent::mytokenEvent::function::test ():
|
|
|
+block#0 entry:
|
|
|
+ ptr<struct.vector<uint8>> %abi_encoded.temp.0 = alloc ptr<struct.vector<uint8>>[uint32(40)];
|
|
|
+ bytes8 %temp.ssa_ir.1 = bytes8 hex"cc_c9_89_03_bd_da_d5_98";
|
|
|
+ write_buf ptr<struct.vector<uint8>>(%abi_encoded.temp.0) offset:uint32(0) value:bytes8(%temp.ssa_ir.1);
|
|
|
+ write_buf ptr<struct.vector<uint8>>(%abi_encoded.temp.0) offset:uint32(8) value:int256(1);
|
|
|
+ emit event#0 to topics[], data: ptr<struct.vector<uint8>>(%abi_encoded.temp.0);
|
|
|
+ return;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_fmt_string_and_print() {
|
|
|
+ let src = r#"contract Test {
|
|
|
+ function test() public {
|
|
|
+ print("Number: {}".format(123));
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 Test::Test::function::test ():
|
|
|
+block#0 entry:
|
|
|
+ ptr<struct.vector<uint8>> %temp.ssa_ir.2 = ptr<struct.vector<uint8>> hex"4e_75_6d_62_65_72_3a_20";
|
|
|
+ ptr<struct.vector<uint8>> %temp.ssa_ir.1 = fmt_str(ptr<struct.vector<uint8>>(%temp.ssa_ir.2), uint8(123));
|
|
|
+ print ptr<struct.vector<uint8>>(%temp.ssa_ir.1);
|
|
|
+ return;"#,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn test_bitewise_not() {
|
|
|
+ let src = r#"contract Test {
|
|
|
+ function byte_wise_not(bytes14 a) public pure returns (bytes14) {
|
|
|
+ return ~a;
|
|
|
+ }
|
|
|
+ }"#;
|
|
|
+
|
|
|
+ assert_solana_lir_str_eq(
|
|
|
+ src,
|
|
|
+ 0,
|
|
|
+ r#"public function sol#2 Test::Test::function::byte_wise_not__bytes14 (bytes14) returns (bytes14):
|
|
|
+block#0 entry:
|
|
|
+ bytes14 %a = bytes14(arg#0);
|
|
|
+ bytes14 %temp.ssa_ir.2 = ~bytes14(%a);
|
|
|
+ return bytes14(%temp.ssa_ir.2);"#,
|
|
|
+ )
|
|
|
+}
|