diff --git a/compiler/src/yul/mappers/functions.rs b/compiler/src/yul/mappers/functions.rs index f34ea63b80..b463e510b8 100644 --- a/compiler/src/yul/mappers/functions.rs +++ b/compiler/src/yul/mappers/functions.rs @@ -188,10 +188,15 @@ fn emit(context: &Context, stmt: &Node) -> yul::Statement { } fn assert(context: &Context, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::Assert { test, msg: _ } = &stmt.kind { + if let fe::FuncStmt::Assert { test, msg } = &stmt.kind { let test = expressions::expr(context, test); - - return statement! { if (iszero([test])) { (revert(0, 0)) } }; + return match msg { + Some(val) => { + let msg = expressions::expr(context, val); + statement! { if (iszero([test])) { (revert_with_reason_string([msg])) } } + } + None => statement! { if (iszero([test])) { (revert(0, 0)) } }, + }; } unreachable!() diff --git a/compiler/src/yul/runtime/functions/data.rs b/compiler/src/yul/runtime/functions/data.rs index cbca57f8ea..0a0e2730d8 100644 --- a/compiler/src/yul/runtime/functions/data.rs +++ b/compiler/src/yul/runtime/functions/data.rs @@ -400,13 +400,16 @@ pub fn revert_with_reason_string() -> yul::Statement { (mptr := alloc_mstoren(reason_size, 32)) //Read the actual bytes of the reason string and write it to memory - (let reason_bytes := mloadn((add(reason, 32)) , reason_size)) - (mptr := alloc_mstoren(reason_bytes, reason_size)) + //(let reason_bytes := mloadn((add(reason, 32)) , reason_size)) + (let reason_bytes := mcopym((add(reason, 32)) , reason_size)) + + //(mptr := alloc_mstoren(reason_bytes, reason_size)) // Right pad the reason bytes to a multiple of 32 bytes (let padding := sub((ceil32(reason_size)), reason_size)) (mptr := alloc(padding)) (revert(ptr, (add(68, (add(reason_size, padding)))))) + //(revert(reason_bytes, 100)) } } } diff --git a/compiler/tests/features.rs b/compiler/tests/features.rs index 7bc129eb7f..5f41921285 100644 --- a/compiler/tests/features.rs +++ b/compiler/tests/features.rs @@ -75,20 +75,53 @@ fn test_assert() { let exit1 = harness.capture_call(&mut executor, "bar", &[uint_token(4)]); - assert!(matches!( - exit1, - evm::Capture::Exit((evm::ExitReason::Revert(_), _)) - )); + match exit1 { + evm::Capture::Exit((evm::ExitReason::Revert(_), output)) => assert_eq!(output.len(), 0), + _ => panic!("Did not revert correctly"), + } let exit2 = harness.capture_call(&mut executor, "bar", &[uint_token(42)]); assert!(matches!( exit2, evm::Capture::Exit((evm::ExitReason::Succeed(_), _)) - )) + )); + + let exit3 = harness.capture_call(&mut executor, "revert_with_static_string", &[uint_token(4)]); + + match exit3 { + evm::Capture::Exit((evm::ExitReason::Revert(_), output)) => { + assert_eq!(output, encode_error_reason("Must be greater than five")) + } + _ => panic!("Did not revert correctly"), + } + + let reason = "A very looooooooooooooong reason that consumes multiple words"; + let exit4 = harness.capture_call(&mut executor, "revert_with", &[uint_token(4), string_token(&reason)]); + + match exit4 { + evm::Capture::Exit((evm::ExitReason::Revert(_), output)) => { + assert_eq!(output, encode_error_reason(reason)) + } + _ => panic!("Did not revert correctly"), + } }) } + +// [ +// 8, 195, 121, 160, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, +// 65, 32, 118, 101, 114, 121, 32, 108, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 103, 32, 114, 101, 97, 115, 111, 110, 32, 116, 104, 97, 116, 32, 99, 111, 110, 115, 117, 109, 101, 115, 32, 109, 117, 108, 116, 105, 112, 108, 101, 32, 119, 111, 114, 100, 115, 0, 0, 0] + + +// [ +// 8, 195, 121, 160, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + #[rstest(fixture_file, input, expected, case("for_loop_with_static_array.fe", &[], uint_token(30)), case("for_loop_with_break.fe", &[], uint_token(15)), diff --git a/compiler/tests/fixtures/features/assert.fe b/compiler/tests/fixtures/features/assert.fe index fc0c768778..92a4bca3fa 100644 --- a/compiler/tests/fixtures/features/assert.fe +++ b/compiler/tests/fixtures/features/assert.fe @@ -1,3 +1,9 @@ contract Foo: pub def bar(baz: u256): - assert baz > 5 \ No newline at end of file + assert baz > 5 + + pub def revert_with_static_string(baz: u256): + assert baz > 5, "Must be greater than five" + + pub def revert_with(baz: u256, reason: string1000): + assert baz > 5, reason \ No newline at end of file diff --git a/compiler/tests/runtime.rs b/compiler/tests/runtime.rs index 8ad4a97288..0553bb6300 100644 --- a/compiler/tests/runtime.rs +++ b/compiler/tests/runtime.rs @@ -3,6 +3,7 @@ #![cfg(feature = "solc-backend")] use fe_compiler::yul::runtime::functions; use yultsur::*; +use rstest::rstest; mod utils; use fe_analyzer::namespace::types::{ @@ -11,6 +12,7 @@ use fe_analyzer::namespace::types::{ Integer, Struct, }; +use fe_common::utils::keccak; use utils::*; macro_rules! assert_eq { @@ -23,21 +25,27 @@ macro_rules! assert_eq { }; } -#[test] -fn test_revert_with_reason_string() { +#[rstest( + reason, + case("foo"), + case("A very looooooooooooooong reason that consumes multiple words"), +)] +fn test_revert_with_reason_string(reason: &str) { + let reason_id = format!(r#""{}""#, keccak::full(reason.as_bytes())); + with_executor(&|mut executor| { test_runtime_functions_revert( &mut executor, Runtime::default() .with_data( - vec![yul::Data { name: "0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d".to_owned(), value: "foo".to_owned() }] + vec![yul::Data { name: keccak::full(reason.as_bytes()), value: reason.to_owned() }] ) .with_test_statements( statements! { - (let reason := load_data_string((dataoffset("0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d")), (datasize("0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d")))) + (let reason := load_data_string((dataoffset([literal_expression! { (reason_id) }])), (datasize([literal_expression! { (reason_id) }])))) (revert_with_reason_string(reason)) }), - &encode_error_reason("foo") + &encode_error_reason(reason) ); }) }