diff --git a/src/tests/hostapi_tests.rs b/src/tests/hostapi_tests.rs index ec17943..f472539 100644 --- a/src/tests/hostapi_tests.rs +++ b/src/tests/hostapi_tests.rs @@ -360,4 +360,68 @@ mod tests { .unwrap(); runtime.assert_result("f248a1c000000000000000000000000000000000000000000000000000000000"); } + + #[test] + fn test_solidity_revert_short_string() { + let mut runtime = TestRuntime::new( + "test_solidity_revert_short_string", + "target/test_solidity_revert_short_string", + ); + runtime.clear_testdata(); + let yul_code = runtime.compile_solidity_to_yul( + r#" + pragma solidity ^0.8.0; + + contract TestContract { + function test() public { + revert("helloworld"); + } + } + + "#, + "TestContract", + ); + if let Err(err) = &yul_code { + eprintln!("compile to yul error: {err}"); + } + assert!(yul_code.is_ok()); + let yul_code = yul_code.unwrap(); + let _emited_bc = runtime.compile_test_yul(&yul_code).unwrap(); + runtime.set_enable_gas_meter(false); + runtime.deploy(&[]).unwrap(); + runtime.call(&solidity_selector("test()"), &[]).unwrap(); + runtime.assert_revert("08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a68656c6c6f776f726c6400000000000000000000000000000000000000000000"); + } + + #[test] + fn test_solidity_revert_long_string() { + let mut runtime = TestRuntime::new( + "test_solidity_revert_long_string", + "target/test_solidity_revert_long_string", + ); + runtime.clear_testdata(); + let yul_code = runtime.compile_solidity_to_yul( + r#" + pragma solidity ^0.8.0; + + contract TestContract { + function test() public { + revert("helloworld. This is a long revert string longer than 32bytes"); + } + } + + "#, + "TestContract", + ); + if let Err(err) = &yul_code { + eprintln!("compile to yul error: {err}"); + } + assert!(yul_code.is_ok()); + let yul_code = yul_code.unwrap(); + let _emited_bc = runtime.compile_test_yul(&yul_code).unwrap(); + runtime.set_enable_gas_meter(false); + runtime.deploy(&[]).unwrap(); + runtime.call(&solidity_selector("test()"), &[]).unwrap(); + runtime.assert_revert("08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003c68656c6c6f776f726c642e20546869732069732061206c6f6e672072657665727420737472696e67206c6f6e676572207468616e203332627974657300000000"); + } } diff --git a/src/tests/test_helper.rs b/src/tests/test_helper.rs index 553301f..2ff16d9 100644 --- a/src/tests/test_helper.rs +++ b/src/tests/test_helper.rs @@ -15,6 +15,7 @@ use rand::distr::Alphanumeric; use rand::Rng; use std::fs::File; use std::io::Write; +use std::path::Path; use std::process::Command; use tempfile::tempdir; @@ -134,6 +135,56 @@ impl TestRuntime { } } + #[allow(unused)] + pub fn compile_solidity_to_yul( + &mut self, + solidity_code: &str, + contract_name: &str, + ) -> Result { + // avoid using the invalid contract name(not strict check) + // this is because solc will generate yul files with basename of contract name + // but we don't known the name + assert!(solidity_code.contains(&format!("contract {contract_name}"))); + + // 1. create tmp file to store the solidity code + let tmp_dir = Path::new(&self.output_dir); + let sol_path = tmp_dir.join("input.sol"); + let yul_file_basename = &format!("{contract_name}.yul"); + let yul_path = tmp_dir.join(yul_file_basename); + println!( + "tmp_dir: {:?}, yul_path: {:?}", + tmp_dir.as_os_str(), + yul_path.as_os_str() + ); + + if !sol_path.exists() { + let mut sol_file = File::create(&sol_path).map_err(|e| e.to_string())?; + sol_file + .write_all(solidity_code.as_bytes()) + .map_err(|e| e.to_string())?; + } + // There are multiple output files, and the output file names are related to the Solidity contract name. + // 2. using `solc` to compile, + // eg. solc --ir --optimize-yul -o . --overwrite input.sol + let output = Command::new("solc") + .current_dir(tmp_dir) + .arg("--ir") + .arg("--optimize-yul") + .arg("--overwrite") + .arg("-o") + .arg(".") + .arg("input.sol") + .output() + .map_err(|e| e.to_string())?; + if !output.status.success() { + return Err(String::from_utf8_lossy(&output.stderr).to_string()); + } + // 3. read the generated yul file content + let yul_content = std::fs::read_to_string(&yul_path) + .map_err(|e| format!("Failed to read yul file: {}", e))?; + Ok(yul_content) + } + #[allow(unused)] pub fn set_sender(&mut self, sender: Option) { self.sender = sender; diff --git a/src/yul2ir/context.rs b/src/yul2ir/context.rs index e84fc85..df88f63 100644 --- a/src/yul2ir/context.rs +++ b/src/yul2ir/context.rs @@ -510,8 +510,8 @@ impl<'ctx> Yul2IRContext<'ctx> { } pub fn string_literal(&self, value: &str) -> IntValue<'ctx> { - if value.len() > 31 { - // panic!("String literal length exceeds 31 bytes: {}", value); + if value.len() > 32 { + // panic!("String literal length exceeds 32 bytes: {}", value); // String literals used in linker symbol, data offset, and data size instructions // don't need to be processed by string_literal function // After modifying the logic in instruction.rs, we can restore the panic here