From 278bb8cd19837898d3750918f0be30a85af916d9 Mon Sep 17 00:00:00 2001 From: Daniel Frederico Lins Leite Date: Fri, 22 Nov 2024 08:02:21 -0300 Subject: [PATCH] forc test single-step until jump point instead of patching binary (#6731) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Fixes https://github.com/FuelLabs/sway/issues/6720. Since configurables started to use encoding v1, it is not possible to use configurables inside tests, because `forc test` patches the binary forcing a jump into the test function before configurables are initialized. This PR fixes this changing the approach from patching the binary, to single-stepping the initialization and them manually changing the `PC` register to the first instruction of the test. Performance is acceptable, a test with a lot of configurables takes `572.382µs`. ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [ ] If my change requires substantial documentation changes, I have [requested support from the DevRel team](https://github.com/FuelLabs/devrel-requests/issues/new/choose) - [x] I have added tests that prove my fix is effective or that my feature works. - [ ] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --------- Co-authored-by: Joshua Batty Co-authored-by: Kaya Gökalp Co-authored-by: IGI-111 --- forc-test/src/execute.rs | 157 ++++++---- .../language/configurable_tests/Forc.lock | 13 + .../language/configurable_tests/Forc.toml | 8 + .../configurable_tests/json_abi_oracle.json | 284 ++++++++++++++++++ .../json_abi_oracle_new_encoding.json | 228 ++++++++++++++ .../language/configurable_tests/src/main.sw | 83 +++++ .../language/configurable_tests/test.toml | 1 + 7 files changed, 721 insertions(+), 53 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle_new_encoding.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/test.toml diff --git a/forc-test/src/execute.rs b/forc-test/src/execute.rs index 76e89324903..a0423caba12 100644 --- a/forc-test/src/execute.rs +++ b/forc-test/src/execute.rs @@ -5,6 +5,9 @@ use crate::TEST_METADATA_SEED; use forc_pkg::PkgTestEntry; use fuel_tx::{self as tx, output::contract::Contract, Chargeable, Finalizable}; use fuel_vm::error::InterpreterError; +use fuel_vm::fuel_asm; +use fuel_vm::prelude::Instruction; +use fuel_vm::prelude::RegId; use fuel_vm::{ self as vm, checked_transaction::builder::TransactionBuilderExt, @@ -27,6 +30,8 @@ pub struct TestExecutor { pub tx: vm::checked_transaction::Ready, pub test_entry: PkgTestEntry, pub name: String, + pub jump_instruction_index: usize, + pub relative_jump_in_bytes: u32, } /// The result of executing a test with breakpoints enabled. @@ -41,15 +46,16 @@ pub enum DebugResult { impl TestExecutor { pub fn build( bytecode: &[u8], - test_offset: u32, + test_instruction_index: u32, test_setup: TestSetup, test_entry: &PkgTestEntry, name: String, ) -> anyhow::Result { let storage = test_setup.storage().clone(); - // Patch the bytecode to jump to the relevant test. - let bytecode = patch_test_bytecode(bytecode, test_offset).into_owned(); + // Find the instruction which we will jump into the + // specified test + let jump_instruction_index = find_jump_instruction_index(bytecode); // Create a transaction to execute the test function. let script_input_data = vec![]; @@ -68,7 +74,7 @@ impl TestExecutor { let block_height = (u32::MAX >> 1).into(); let gas_price = 0; - let mut tx_builder = tx::TransactionBuilder::script(bytecode, script_input_data); + let mut tx_builder = tx::TransactionBuilder::script(bytecode.to_vec(), script_input_data); let params = maxed_consensus_params(); @@ -126,23 +132,72 @@ impl TestExecutor { tx, test_entry: test_entry.clone(), name, + jump_instruction_index, + relative_jump_in_bytes: (test_instruction_index - jump_instruction_index as u32) + * Instruction::SIZE as u32, }) } + // single-step until the jump-to-test instruction, then + // jump into the first instruction of the test + fn single_step_until_test(&mut self) -> ProgramState { + let jump_pc = (self.jump_instruction_index * Instruction::SIZE) as u64; + + let old_single_stepping = self.interpreter.single_stepping(); + self.interpreter.set_single_stepping(true); + let mut state = { + let transition = self.interpreter.transact(self.tx.clone()); + Ok(*transition.unwrap().state()) + }; + + loop { + match state { + // if the VM fails, we interpret as a revert + Err(_) => { + break ProgramState::Revert(0); + } + Ok( + state @ ProgramState::Return(_) + | state @ ProgramState::ReturnData(_) + | state @ ProgramState::Revert(_), + ) => break state, + Ok( + s @ ProgramState::RunProgram(eval) | s @ ProgramState::VerifyPredicate(eval), + ) => { + // time to jump into the specified test + if let Some(b) = eval.breakpoint() { + if b.pc() == jump_pc { + self.interpreter.registers_mut()[RegId::PC] += + self.relative_jump_in_bytes as u64; + self.interpreter.set_single_stepping(old_single_stepping); + break s; + } + } + + state = self.interpreter.resume(); + } + } + } + } + /// Execute the test with breakpoints enabled. pub fn start_debugging(&mut self) -> anyhow::Result { let start = std::time::Instant::now(); - let transition = self + + let _ = self.single_step_until_test(); + let state = self .interpreter - .transact(self.tx.clone()) - .map_err(|err: InterpreterError<_>| anyhow::anyhow!(err))?; - let state = *transition.state(); + .resume() + .map_err(|err: InterpreterError<_>| { + anyhow::anyhow!("VM failed to resume. {:?}", err) + })?; if let ProgramState::RunProgram(DebugEval::Breakpoint(breakpoint)) = state { // A breakpoint was hit, so we tell the client to stop. return Ok(DebugResult::Breakpoint(breakpoint.pc())); } + let duration = start.elapsed(); - let (gas_used, logs) = Self::get_gas_and_receipts(transition.receipts().to_vec())?; + let (gas_used, logs) = Self::get_gas_and_receipts(self.interpreter.receipts().to_vec())?; let span = self.test_entry.span.clone(); let file_path = self.test_entry.file_path.clone(); let condition = self.test_entry.pass_condition.clone(); @@ -192,14 +247,27 @@ impl TestExecutor { pub fn execute(&mut self) -> anyhow::Result { let start = std::time::Instant::now(); - let transition = self - .interpreter - .transact(self.tx.clone()) - .map_err(|err: InterpreterError<_>| anyhow::anyhow!(err))?; - let state = *transition.state(); + + let mut state = Ok(self.single_step_until_test()); + + // Run test until its end + loop { + match state { + Err(_) => { + state = Ok(ProgramState::Revert(0)); + break; + } + Ok( + ProgramState::Return(_) | ProgramState::ReturnData(_) | ProgramState::Revert(_), + ) => break, + Ok(ProgramState::RunProgram(_) | ProgramState::VerifyPredicate(_)) => { + state = self.interpreter.resume(); + } + } + } let duration = start.elapsed(); - let (gas_used, logs) = Self::get_gas_and_receipts(transition.receipts().to_vec())?; + let (gas_used, logs) = Self::get_gas_and_receipts(self.interpreter.receipts().to_vec())?; let span = self.test_entry.span.clone(); let file_path = self.test_entry.file_path.clone(); let condition = self.test_entry.pass_condition.clone(); @@ -209,7 +277,7 @@ impl TestExecutor { file_path, duration, span, - state, + state: state.unwrap(), condition, logs, gas_used, @@ -237,42 +305,25 @@ impl TestExecutor { } } -/// Given some bytecode and an instruction offset for some test's desired entry point, patch the -/// bytecode with a `JI` (jump) instruction to jump to the desired test. -/// -/// We want to splice in the `JI` only after the initial data section setup is complete, and only -/// if the entry point doesn't begin exactly after the data section setup. -/// -/// The following is how the beginning of the bytecode is laid out: -/// -/// ```ignore -/// [ 0] ji i(4 + 2) ; Jumps to the data section setup. -/// [ 1] noop -/// [ 2] DATA_SECTION_OFFSET[0..32] -/// [ 3] DATA_SECTION_OFFSET[32..64] -/// [ 4] CONFIGURABLES_OFFSET[0..32] -/// [ 5] CONFIGURABLES_OFFSET[32..64] -/// [ 6] lw $ds $is 1 ; The data section setup, i.e. where the first ji lands. -/// [ 7] add $$ds $$ds $is -/// [ 8] ; This is where we want to jump from to our test code! -/// ``` -fn patch_test_bytecode(bytecode: &[u8], test_offset: u32) -> std::borrow::Cow<[u8]> { - // Each instruction is 4 bytes, - // so we divide the total byte-size by 4 to get the instruction offset. - const PROGRAM_START_INST_OFFSET: u32 = (sway_core::PRELUDE_SIZE_IN_BYTES / 4) as u32; - const PROGRAM_START_BYTE_OFFSET: usize = sway_core::PRELUDE_SIZE_IN_BYTES; - - // If our desired entry point is the program start, no need to jump. - if test_offset == PROGRAM_START_INST_OFFSET { - return std::borrow::Cow::Borrowed(bytecode); - } +fn find_jump_instruction_index(bytecode: &[u8]) -> usize { + // Search first `move $$locbase $sp` + // This will be `__entry` for script/predicate/contract using encoding v1; + // `main` for script/predicate using encoding v0; + // or the first function for libraries + // MOVE R59 $sp ;; [26, 236, 80, 0] + let a = vm::fuel_asm::op::move_(59, fuel_asm::RegId::SP).to_bytes(); - // Create the jump instruction and splice it into the bytecode. - let ji = vm::fuel_asm::op::ji(test_offset); - let ji_bytes = ji.to_bytes(); - let start = PROGRAM_START_BYTE_OFFSET; - let end = start + ji_bytes.len(); - let mut patched = bytecode.to_vec(); - patched.splice(start..end, ji_bytes); - std::borrow::Cow::Owned(patched) + // for contracts using encoding v0 + // search the first `lw $r0 $fp i73` + // which is the start of the fn selector + // LW $writable $fp 0x49 ;; [93, 64, 96, 73] + let b = vm::fuel_asm::op::lw(fuel_asm::RegId::WRITABLE, fuel_asm::RegId::FP, 73).to_bytes(); + + bytecode + .chunks(Instruction::SIZE) + .position(|instruction| { + let instruction: [u8; 4] = instruction.try_into().unwrap(); + instruction == a || instruction == b + }) + .unwrap() } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/Forc.lock new file mode 100644 index 00000000000..688eb152964 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "configurable_tests" +source = "member" +dependencies = ["std"] + +[[package]] +name = "core" +source = "path+from-root-CEAD1EF3DC39BB76" + +[[package]] +name = "std" +source = "path+from-root-CEAD1EF3DC39BB76" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/Forc.toml new file mode 100644 index 00000000000..7fff3f8b575 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "configurable_tests" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle.json new file mode 100644 index 00000000000..096b1d487c9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle.json @@ -0,0 +1,284 @@ +{ + "configurables": [ + { + "configurableType": { + "name": "", + "type": 5, + "typeArguments": null + }, + "name": "BOOL", + "offset": 3392 + }, + { + "configurableType": { + "name": "", + "type": 13, + "typeArguments": null + }, + "name": "U8", + "offset": 3528 + }, + { + "configurableType": { + "name": "", + "type": 13, + "typeArguments": null + }, + "name": "ANOTHER_U8", + "offset": 3320 + }, + { + "configurableType": { + "name": "", + "type": 9, + "typeArguments": null + }, + "name": "U16", + "offset": 3472 + }, + { + "configurableType": { + "name": "", + "type": 11, + "typeArguments": null + }, + "name": "U32", + "offset": 3512 + }, + { + "configurableType": { + "name": "", + "type": 11, + "typeArguments": null + }, + "name": "U64", + "offset": 3520 + }, + { + "configurableType": { + "name": "", + "type": 10, + "typeArguments": null + }, + "name": "U256", + "offset": 3480 + }, + { + "configurableType": { + "name": "", + "type": 4, + "typeArguments": null + }, + "name": "B256", + "offset": 3360 + }, + { + "configurableType": { + "name": "", + "type": 8, + "typeArguments": [] + }, + "name": "CONFIGURABLE_STRUCT", + "offset": 3432 + }, + { + "configurableType": { + "name": "", + "type": 6, + "typeArguments": [] + }, + "name": "CONFIGURABLE_ENUM_A", + "offset": 3400 + }, + { + "configurableType": { + "name": "", + "type": 6, + "typeArguments": [] + }, + "name": "CONFIGURABLE_ENUM_B", + "offset": 3416 + }, + { + "configurableType": { + "name": "", + "type": 2, + "typeArguments": null + }, + "name": "ARRAY_BOOL", + "offset": 3328 + }, + { + "configurableType": { + "name": "", + "type": 3, + "typeArguments": null + }, + "name": "ARRAY_U64", + "offset": 3336 + }, + { + "configurableType": { + "name": "", + "type": 1, + "typeArguments": null + }, + "name": "TUPLE_BOOL_U64", + "offset": 3456 + }, + { + "configurableType": { + "name": "", + "type": 7, + "typeArguments": null + }, + "name": "STR_4", + "offset": 3448 + } + ], + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + } + } + ], + "loggedTypes": [], + "messagesTypes": [], + "types": [ + { + "components": [], + "type": "()", + "typeId": 0, + "typeParameters": null + }, + { + "components": [ + { + "name": "__tuple_element", + "type": 5, + "typeArguments": null + }, + { + "name": "__tuple_element", + "type": 12, + "typeArguments": null + } + ], + "type": "(_, _)", + "typeId": 1, + "typeParameters": null + }, + { + "components": [ + { + "name": "__array_element", + "type": 5, + "typeArguments": null + } + ], + "type": "[_; 3]", + "typeId": 2, + "typeParameters": null + }, + { + "components": [ + { + "name": "__array_element", + "type": 12, + "typeArguments": null + } + ], + "type": "[_; 3]", + "typeId": 3, + "typeParameters": null + }, + { + "components": null, + "type": "b256", + "typeId": 4, + "typeParameters": null + }, + { + "components": null, + "type": "bool", + "typeId": 5, + "typeParameters": null + }, + { + "components": [ + { + "name": "A", + "type": 5, + "typeArguments": null + }, + { + "name": "B", + "type": 12, + "typeArguments": null + } + ], + "type": "enum ConfigurableEnum", + "typeId": 6, + "typeParameters": null + }, + { + "components": null, + "type": "str[4]", + "typeId": 7, + "typeParameters": null + }, + { + "components": [ + { + "name": "a", + "type": 5, + "typeArguments": null + }, + { + "name": "b", + "type": 12, + "typeArguments": null + } + ], + "type": "struct ConfigurableStruct", + "typeId": 8, + "typeParameters": null + }, + { + "components": null, + "type": "u16", + "typeId": 9, + "typeParameters": null + }, + { + "components": null, + "type": "u256", + "typeId": 10, + "typeParameters": null + }, + { + "components": null, + "type": "u32", + "typeId": 11, + "typeParameters": null + }, + { + "components": null, + "type": "u64", + "typeId": 12, + "typeParameters": null + }, + { + "components": null, + "type": "u8", + "typeId": 13, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle_new_encoding.json new file mode 100644 index 00000000000..2daeaffdc6a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle_new_encoding.json @@ -0,0 +1,228 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "type": "()" + }, + { + "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", + "metadataTypeId": 0, + "type": "(bool, u64)" + }, + { + "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", + "metadataTypeId": 1, + "type": "[bool; 3]" + }, + { + "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", + "metadataTypeId": 2, + "type": "[u64; 3]" + }, + { + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "type": "b256" + }, + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "type": "bool" + }, + { + "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", + "metadataTypeId": 3, + "type": "enum ConfigurableEnum" + }, + { + "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", + "type": "str[4]" + }, + { + "concreteTypeId": "81fc10c4681a3271cf2d66b2ec6fbc8ed007a442652930844fcf11818c295bff", + "metadataTypeId": 4, + "type": "struct ConfigurableStruct" + }, + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "type": "u16" + }, + { + "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", + "type": "u256" + }, + { + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", + "type": "u32" + }, + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "type": "u8" + } + ], + "configurables": [ + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "name": "BOOL", + "offset": 6896 + }, + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "name": "U8", + "offset": 7088 + }, + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "name": "ANOTHER_U8", + "offset": 6824 + }, + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "name": "U16", + "offset": 7032 + }, + { + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", + "name": "U32", + "offset": 7072 + }, + { + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", + "name": "U64", + "offset": 7080 + }, + { + "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", + "name": "U256", + "offset": 7040 + }, + { + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "name": "B256", + "offset": 6864 + }, + { + "concreteTypeId": "81fc10c4681a3271cf2d66b2ec6fbc8ed007a442652930844fcf11818c295bff", + "name": "CONFIGURABLE_STRUCT", + "offset": 6984 + }, + { + "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", + "name": "CONFIGURABLE_ENUM_A", + "offset": 6904 + }, + { + "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", + "name": "CONFIGURABLE_ENUM_B", + "offset": 6944 + }, + { + "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", + "name": "ARRAY_BOOL", + "offset": 6832 + }, + { + "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", + "name": "ARRAY_U64", + "offset": 6840 + }, + { + "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", + "name": "TUPLE_BOOL_U64", + "offset": 7016 + }, + { + "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", + "name": "STR_4", + "offset": 7008 + }, + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "name": "NOT_USED", + "offset": 7000 + } + ], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [ + { + "components": [ + { + "name": "__tuple_element", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "name": "__tuple_element", + "typeId": 5 + } + ], + "metadataTypeId": 0, + "type": "(_, _)" + }, + { + "components": [ + { + "name": "__array_element", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ], + "metadataTypeId": 1, + "type": "[_; 3]" + }, + { + "components": [ + { + "name": "__array_element", + "typeId": 5 + } + ], + "metadataTypeId": 2, + "type": "[_; 3]" + }, + { + "components": [ + { + "name": "A", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "name": "B", + "typeId": 5 + }, + { + "name": "C", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ], + "metadataTypeId": 3, + "type": "enum ConfigurableEnum" + }, + { + "components": [ + { + "name": "a", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "name": "b", + "typeId": 5 + } + ], + "metadataTypeId": 4, + "type": "struct ConfigurableStruct" + }, + { + "metadataTypeId": 5, + "type": "u64" + } + ], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/src/main.sw new file mode 100644 index 00000000000..a0e5d11a345 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/src/main.sw @@ -0,0 +1,83 @@ +script; + +use std::hash::*; + +struct ConfigurableStruct { + a: bool, + b: u64, +} + +enum ConfigurableEnum { + A: bool, + B: u64, + C: b256 +} + +impl core::ops::Eq for ConfigurableEnum { + fn eq(self, other: ConfigurableEnum) -> bool { + match (self, other) { + (ConfigurableEnum::A(inner1), ConfigurableEnum::A(inner2)) => inner1 == inner2, + (ConfigurableEnum::B(inner1), ConfigurableEnum::B(inner2)) => inner1 == inner2, + _ => false, + } + } +} + +type AnotherU8 = u8; + +configurable { + BOOL: bool = true, + U8: u8 = 1, + ANOTHER_U8: AnotherU8 = 3, + U16: u16 = 2, + U32: u32 = 3, + U64: u32 = 4, + U256: u256 = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu256, + B256: b256 = 0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, + CONFIGURABLE_STRUCT: ConfigurableStruct = ConfigurableStruct { a: true, b: 5 }, + CONFIGURABLE_ENUM_A: ConfigurableEnum = ConfigurableEnum::A(true), + CONFIGURABLE_ENUM_B: ConfigurableEnum = ConfigurableEnum::B(12), + ARRAY_BOOL: [bool; 3] = [true, false, true], + ARRAY_U64: [u64; 3] = [9, 8, 7], + TUPLE_BOOL_U64: (bool, u64) = (true, 11), + STR_4: str[4] = __to_str_array("abcd"), + + NOT_USED: u8 = 1 +} + +fn main() { +} + +#[test] +fn t() { + assert(BOOL == true); + assert(U8 == 1); + assert(ANOTHER_U8 == 3); + assert(U16 == 2); + assert(U32 == 3); + assert(U64 == 4); + assert(U256 == 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu256); + assert(B256 == 0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB); + assert(CONFIGURABLE_STRUCT.a == true); + assert(CONFIGURABLE_STRUCT.b == 5); + assert(CONFIGURABLE_ENUM_A == ConfigurableEnum::A(true)); + assert(CONFIGURABLE_ENUM_B == ConfigurableEnum::B(12)); + assert(ARRAY_BOOL[0] == true); + assert(ARRAY_BOOL[1] == false); + assert(ARRAY_BOOL[2] == true); + assert(ARRAY_U64[0] == 9); + assert(ARRAY_U64[1] == 8); + assert(ARRAY_U64[2] == 7); + assert(TUPLE_BOOL_U64.0 == true); + assert(TUPLE_BOOL_U64.1 == 11); + assert(sha256_str_array(STR_4) == sha256("abcd")); + + // Assert address do not change + let addr_1 = asm(addr: __addr_of(&BOOL)) { + addr: u64 + }; + let addr_2 = asm(addr: __addr_of(&BOOL)) { + addr: u64 + }; + assert(addr_1 == addr_2); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/test.toml new file mode 100644 index 00000000000..0f3f6d7e866 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/test.toml @@ -0,0 +1 @@ +category = "unit_tests_pass"