From 69a42cf111ba8ca17248ccaf5012159cc48bc505 Mon Sep 17 00:00:00 2001 From: Mike Voronov Date: Wed, 20 Apr 2022 11:43:46 +0300 Subject: [PATCH] Introduce new for non iterable scalars (#248) --- CHANGELOG.md | 8 + Cargo.lock | 4 +- air-interpreter/Cargo.toml | 2 +- air/Cargo.toml | 2 +- .../air/ap/apply_to_arguments.rs | 2 +- .../execution_step/air/call/resolved_call.rs | 6 +- air/src/execution_step/air/fold/utils.rs | 4 +- air/src/execution_step/air/new.rs | 29 +- .../execution_step/errors/catchable_errors.rs | 5 + .../errors/uncatchable_errors.rs | 13 +- .../execution_context/scalar_variables.rs | 100 +++++-- .../execution_step/lambda_applier/applier.rs | 4 +- air/src/execution_step/resolver/resolve.rs | 2 +- .../features/scopes/scalars_scope.rs | 79 ++++++ air/tests/test_module/instructions/call.rs | 2 +- air/tests/test_module/instructions/new.rs | 255 ++++++++++++++++++ air/tests/test_module/instructions/xor.rs | 2 +- .../air-parser/src/parser/tests/new.rs | 1 - 18 files changed, 473 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d60efc8a..22a518a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## Version 0.23.0 (2021-04-19) + +[PR 248](https://github.com/fluencelabs/aquavm/pull/248): +Introduced new for scalars + +[PR 244](https://github.com/fluencelabs/aquavm/pull/244): +Stack size was increased to 50 MiB + ## Version 0.22.0 (2021-04-14) [PR 243](https://github.com/fluencelabs/aquavm/pull/243): diff --git a/Cargo.lock b/Cargo.lock index aaa3ee1b..4b660cb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "air" -version = "0.22.0" +version = "0.23.0" dependencies = [ "air-execution-info-collector", "air-interpreter-data", @@ -52,7 +52,7 @@ version = "0.1.0" [[package]] name = "air-interpreter" -version = "0.22.0" +version = "0.23.0" dependencies = [ "air", "air-log-targets", diff --git a/air-interpreter/Cargo.toml b/air-interpreter/Cargo.toml index fcbfcaa4..5df996a0 100644 --- a/air-interpreter/Cargo.toml +++ b/air-interpreter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "air-interpreter" -version = "0.22.0" +version = "0.23.0" description = "Crate-wrapper for air" authors = ["Fluence Labs"] edition = "2018" diff --git a/air/Cargo.toml b/air/Cargo.toml index 2c55079b..b9e3f2ba 100644 --- a/air/Cargo.toml +++ b/air/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "air" -version = "0.22.0" +version = "0.23.0" description = "Interpreter of AIR scripts intended to coordinate request flow in the Fluence network" authors = ["Fluence Labs"] edition = "2018" diff --git a/air/src/execution_step/air/ap/apply_to_arguments.rs b/air/src/execution_step/air/ap/apply_to_arguments.rs index a6dd4f83..aa9b9cb6 100644 --- a/air/src/execution_step/air/ap/apply_to_arguments.rs +++ b/air/src/execution_step/air/ap/apply_to_arguments.rs @@ -82,7 +82,7 @@ fn apply_scalar_impl( ) -> ExecutionResult { use crate::execution_step::ScalarRef; - let scalar = exec_ctx.scalars.get(scalar_name)?; + let scalar = exec_ctx.scalars.get_value(scalar_name)?; let mut result = match scalar { ScalarRef::Value(result) => result.clone(), diff --git a/air/src/execution_step/air/call/resolved_call.rs b/air/src/execution_step/air/call/resolved_call.rs index 9ee97a7a..a12cc81d 100644 --- a/air/src/execution_step/air/call/resolved_call.rs +++ b/air/src/execution_step/air/call/resolved_call.rs @@ -213,12 +213,12 @@ fn check_output_name(output: &ast::CallOutputValue<'_>, exec_ctx: &ExecutionCtx< _ => return Ok(()), }; - match exec_ctx.scalars.get(scalar_name) { + match exec_ctx.scalars.get_value(scalar_name) { Ok(ScalarRef::Value(_)) => { - if exec_ctx.scalars.shadowing_allowed() { + if exec_ctx.scalars.variable_could_be_set(scalar_name) { Ok(()) } else { - Err(UncatchableError::MultipleVariablesFound(scalar_name.to_string()).into()) + Err(UncatchableError::ShadowingIsNotAllowed(scalar_name.to_string()).into()) } } Ok(ScalarRef::IterableValue(_)) => Err(UncatchableError::IterableShadowing(scalar_name.to_string()).into()), diff --git a/air/src/execution_step/air/fold/utils.rs b/air/src/execution_step/air/fold/utils.rs index eb52b0ca..3c883a59 100644 --- a/air/src/execution_step/air/fold/utils.rs +++ b/air/src/execution_step/air/fold/utils.rs @@ -71,7 +71,7 @@ fn create_scalar_iterable<'ctx>( exec_ctx: &ExecutionCtx<'ctx>, variable_name: &str, ) -> ExecutionResult { - match exec_ctx.scalars.get(variable_name)? { + match exec_ctx.scalars.get_value(variable_name)? { ScalarRef::Value(call_result) => from_call_result(call_result.clone(), variable_name), ScalarRef::IterableValue(fold_state) => { let iterable_value = fold_state.iterable.peek().unwrap(); @@ -115,7 +115,7 @@ fn create_scalar_lambda_iterable<'ctx>( ) -> ExecutionResult { use crate::execution_step::lambda_applier::select_from_scalar; - match exec_ctx.scalars.get(scalar_name)? { + match exec_ctx.scalars.get_value(scalar_name)? { ScalarRef::Value(variable) => { let jvalues = select_from_scalar(&variable.result, lambda.iter(), exec_ctx)?; let tetraplet = variable.tetraplet.deref().clone(); diff --git a/air/src/execution_step/air/new.rs b/air/src/execution_step/air/new.rs index 1ef9ad96..afb192b7 100644 --- a/air/src/execution_step/air/new.rs +++ b/air/src/execution_step/air/new.rs @@ -32,9 +32,17 @@ impl<'i> super::ExecutableInstruction<'i> for New<'i> { // any error. It's highly important to distinguish between global and restricted streams // at the end of execution to make a correct data. let instruction_result = self.instruction.execute(exec_ctx, trace_ctx); - epilog(self, exec_ctx); + let epilog_result = epilog(self, exec_ctx); - instruction_result + match (instruction_result, epilog_result) { + (Ok(()), Ok(())) => Ok(()), + // instruction error has higher priority over epilog result error, + // because epilog returns "utility" errors that normally (meaning that AquaVM + // scalar handling code doesn't contain errors) shouldn't happen, + // additionally see test new_with_randomly_set_scalars_in_fold_2 + (err @ Err(_), _) => err, + (_, err @ Err(_)) => err, + } } } @@ -45,20 +53,21 @@ fn prolog<'i>(new: &New<'i>, exec_ctx: &mut ExecutionCtx<'i>) { let iteration = exec_ctx.tracker.new_tracker.get_iteration(position); exec_ctx.streams.meet_scope_start(stream.name, new.span, iteration); } - // noop - Variable::Scalar(_) => {} + Variable::Scalar(scalar) => exec_ctx.scalars.meet_new_start(scalar.name), } exec_ctx.tracker.meet_new(position); } -fn epilog<'i>(new: &New<'i>, exec_ctx: &mut ExecutionCtx<'i>) { +fn epilog<'i>(new: &New<'i>, exec_ctx: &mut ExecutionCtx<'i>) -> ExecutionResult<()> { let position = new.span.left; match &new.variable { - Variable::Stream(stream) => exec_ctx - .streams - .meet_scope_end(stream.name.to_string(), position as u32), - // noop - Variable::Scalar(_) => {} + Variable::Stream(stream) => { + exec_ctx + .streams + .meet_scope_end(stream.name.to_string(), position as u32); + Ok(()) + } + Variable::Scalar(scalar) => exec_ctx.scalars.meet_new_end(scalar.name), } } diff --git a/air/src/execution_step/errors/catchable_errors.rs b/air/src/execution_step/errors/catchable_errors.rs index 336934f2..f1ad4edc 100644 --- a/air/src/execution_step/errors/catchable_errors.rs +++ b/air/src/execution_step/errors/catchable_errors.rs @@ -82,6 +82,11 @@ pub enum CatchableError { /// This error type is produced by a fail instruction that tries to throw a scalar that have inappropriate type. #[error(transparent)] InvalidLastErrorObjectError(#[from] LastErrorObjectError), + + /// A new with this variable name was met and right after that it was accessed + /// that is prohibited. + #[error("variable with name '{0}' was cleared by new and then wasn't set")] + VariableWasNotInitializedAfterNew(String), } impl From for Rc { diff --git a/air/src/execution_step/errors/uncatchable_errors.rs b/air/src/execution_step/errors/uncatchable_errors.rs index 7ec57ae1..e377809a 100644 --- a/air/src/execution_step/errors/uncatchable_errors.rs +++ b/air/src/execution_step/errors/uncatchable_errors.rs @@ -55,9 +55,16 @@ pub enum UncatchableError { #[error("ap result {0:?} doesn't match with corresponding instruction")] ApResultNotCorrespondToInstr(MergerApResult), - /// Multiple values for such name found. - #[error("multiple variables found for name '{0}' in data")] - MultipleVariablesFound(String), + /// Variable shadowing is not allowed, usually it's thrown when a AIR tries to assign value + /// for a variable not in a fold block or in a global scope but not right after new. + #[error("trying to shadow variable '{0}', but shadowing is allowed only inside fold blocks")] + ShadowingIsNotAllowed(String), + + /// This error occurred when new tries to pop up a variable at the end, but scalar state doesn't + /// contain an appropriate variable. It should be considered as an internal error and shouldn't + /// be caught by a xor instruction. + #[error("new end block tries to pop up a variable '{scalar_name}' that wasn't defined at depth {depth}")] + ScalarsStateCorrupted { scalar_name: String, depth: usize }, } impl ToErrorCode for UncatchableError { diff --git a/air/src/execution_step/execution_context/scalar_variables.rs b/air/src/execution_step/execution_context/scalar_variables.rs index 0124953b..8e868b7d 100644 --- a/air/src/execution_step/execution_context/scalar_variables.rs +++ b/air/src/execution_step/execution_context/scalar_variables.rs @@ -103,12 +103,19 @@ pub(crate) struct Scalars<'i> { pub(crate) struct SparseCell { /// Scope depth where the value was set. pub(crate) depth: usize, - pub(crate) value: ValueAggregate, + pub(crate) value: Option, } impl SparseCell { - pub(crate) fn new(depth: usize, value: ValueAggregate) -> Self { - Self { depth, value } + pub(crate) fn from_value(depth: usize, value: ValueAggregate) -> Self { + Self { + depth, + value: Some(value), + } + } + + pub(crate) fn from_met_new(depth: usize) -> Self { + Self { depth, value: None } } } @@ -118,28 +125,29 @@ impl<'i> Scalars<'i> { pub(crate) fn set_value(&mut self, name: impl Into, value: ValueAggregate) -> ExecutionResult { use std::collections::hash_map::Entry::{Occupied, Vacant}; - let shadowing_allowed = self.shadowing_allowed(); - match self.non_iterable_variables.entry(name.into()) { + let name = name.into(); + let variable_could_be_set = self.variable_could_be_set(&name); + match self.non_iterable_variables.entry(name) { Vacant(entry) => { - let cell = SparseCell::new(self.current_depth, value); + let cell = SparseCell::from_value(self.current_depth, value); let cells = NonEmpty::new(cell); entry.insert(cells); Ok(false) } Occupied(entry) => { - if !shadowing_allowed { - return Err(UncatchableError::MultipleVariablesFound(entry.key().clone()).into()); + if !variable_could_be_set { + return Err(UncatchableError::ShadowingIsNotAllowed(entry.key().clone()).into()); } let values = entry.into_mut(); let last_cell = values.last_mut(); if last_cell.depth == self.current_depth { // just rewrite a value if fold level is the same - last_cell.value = value; + last_cell.value = Some(value); Ok(true) } else { - let new_cell = SparseCell::new(self.current_depth, value); + let new_cell = SparseCell::from_value(self.current_depth, value); values.push(new_cell); Ok(false) } @@ -167,7 +175,7 @@ impl<'i> Scalars<'i> { self.iterable_variables.remove(name); } - pub(crate) fn get_value(&'i self, name: &str) -> ExecutionResult<&'i ValueAggregate> { + pub(crate) fn get_non_iterable_value(&'i self, name: &str) -> ExecutionResult> { self.non_iterable_variables .get(name) .and_then(|values| { @@ -175,12 +183,12 @@ impl<'i> Scalars<'i> { let value_not_invalidated = !self.invalidated_depths.contains(&last_cell.depth); if value_not_invalidated { - Some(&last_cell.value) + Some(last_cell.value.as_ref()) } else { None } }) - .ok_or_else(|| Rc::new(CatchableError::VariableNotFound(name.to_string())).into()) + .ok_or_else(|| ExecutionError::Catchable(Rc::new(CatchableError::VariableNotFound(name.to_string())))) } pub(crate) fn get_iterable_mut(&mut self, name: &str) -> ExecutionResult<&mut FoldState<'i>> { @@ -189,13 +197,14 @@ impl<'i> Scalars<'i> { .ok_or_else(|| UncatchableError::FoldStateNotFound(name.to_string()).into()) } - pub(crate) fn get(&'i self, name: &str) -> ExecutionResult> { - let value = self.get_value(name); + pub(crate) fn get_value(&'i self, name: &str) -> ExecutionResult> { + let value = self.get_non_iterable_value(name); let iterable_value = self.iterable_variables.get(name); match (value, iterable_value) { (Err(_), None) => Err(CatchableError::VariableNotFound(name.to_string()).into()), - (Ok(value), None) => Ok(ScalarRef::Value(value)), + (Ok(None), _) => Err(CatchableError::VariableWasNotInitializedAfterNew(name.to_string()).into()), + (Ok(Some(value)), None) => Ok(ScalarRef::Value(value)), (Err(_), Some(iterable_value)) => Ok(ScalarRef::IterableValue(iterable_value)), (Ok(_), Some(_)) => unreachable!("this is checked on the parsing stage"), } @@ -223,6 +232,61 @@ impl<'i> Scalars<'i> { self.cleanup_obsolete_values(); } + pub(crate) fn meet_new_start(&mut self, scalar_name: &str) { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + + let new_cell = SparseCell::from_met_new(self.current_depth); + match self.non_iterable_variables.entry(scalar_name.to_string()) { + Vacant(entry) => { + let ne_vec = NonEmpty::new(new_cell); + entry.insert(ne_vec); + } + Occupied(entry) => { + let entry = entry.into_mut(); + entry.push(new_cell); + } + } + } + + pub(crate) fn meet_new_end(&mut self, scalar_name: &str) -> ExecutionResult<()> { + let current_depth = self.current_depth; + let should_remove_values = self + .non_iterable_variables + .get_mut(scalar_name) + .and_then(|values| { + // carefully check that we're popping up an appropriate value, + // returning None means an error here + match values.pop() { + Some(value) if value.depth == current_depth => Some(false), + Some(_) => None, + // None means that the value was last in a row + None if values.last().depth == current_depth => Some(true), + None => None, + } + }) + .ok_or_else(|| UncatchableError::ScalarsStateCorrupted { + scalar_name: scalar_name.to_string(), + depth: self.current_depth, + }) + .map_err(Into::::into)?; + + if should_remove_values { + self.non_iterable_variables.remove(scalar_name); + } + Ok(()) + } + + pub(crate) fn variable_could_be_set(&self, variable_name: &str) -> bool { + if self.shadowing_allowed() { + return true; + } + + match self.non_iterable_variables.get(variable_name) { + Some(values) => values.last().value.is_none(), + None => false, + } + } + pub(crate) fn shadowing_allowed(&self) -> bool { // shadowing is allowed only inside a fold block, 0 here means that execution flow // is in a global scope @@ -265,8 +329,8 @@ impl<'i> fmt::Display for Scalars<'i> { writeln!(f, "fold_block_id: {}", self.current_depth)?; for (name, _) in self.non_iterable_variables.iter() { - let value = self.get_value(name); - if let Ok(last_value) = value { + let value = self.get_non_iterable_value(name); + if let Ok(Some(last_value)) = value { writeln!(f, "{} => {}", name, last_value.result)?; } } diff --git a/air/src/execution_step/lambda_applier/applier.rs b/air/src/execution_step/lambda_applier/applier.rs index 67c50a11..bf26fe47 100644 --- a/air/src/execution_step/lambda_applier/applier.rs +++ b/air/src/execution_step/lambda_applier/applier.rs @@ -43,7 +43,7 @@ pub(crate) fn select_from_stream<'value, 'i>( })); } ValueAccessor::FieldAccessByScalar { scalar_name } => { - let scalar = exec_ctx.scalars.get(scalar_name)?; + let scalar = exec_ctx.scalars.get_value(scalar_name)?; lambda_to_execution_error!(try_scalar_ref_as_idx(scalar))? } ValueAccessor::Error => unreachable!("should not execute if parsing succeeded. QED."), @@ -74,7 +74,7 @@ pub(crate) fn select_from_scalar<'value, 'accessor, 'i>( value = lambda_to_execution_error!(try_jvalue_with_field_name(value, *field_name))?; } ValueAccessor::FieldAccessByScalar { scalar_name } => { - let scalar = exec_ctx.scalars.get(scalar_name)?; + let scalar = exec_ctx.scalars.get_value(scalar_name)?; value = lambda_to_execution_error!(select_by_scalar(value, scalar))?; } ValueAccessor::Error => unreachable!("should not execute if parsing succeeded. QED."), diff --git a/air/src/execution_step/resolver/resolve.rs b/air/src/execution_step/resolver/resolve.rs index 54a5aa1e..385bbb67 100644 --- a/air/src/execution_step/resolver/resolve.rs +++ b/air/src/execution_step/resolver/resolve.rs @@ -92,7 +92,7 @@ pub(crate) fn resolve_variable<'ctx, 'i>( use crate::execution_step::boxed_value::StreamJvaluableIngredients; match variable { - Variable::Scalar { name, .. } => Ok(ctx.scalars.get(name)?.into_jvaluable()), + Variable::Scalar { name, .. } => Ok(ctx.scalars.get_value(name)?.into_jvaluable()), Variable::Stream { name, generation, diff --git a/air/tests/test_module/features/scopes/scalars_scope.rs b/air/tests/test_module/features/scopes/scalars_scope.rs index 5ccb0bec..96b8c509 100644 --- a/air/tests/test_module/features/scopes/scalars_scope.rs +++ b/air/tests/test_module/features/scopes/scalars_scope.rs @@ -14,6 +14,8 @@ * limitations under the License. */ +use air::CatchableError; +use air::ExecutionError; use air_test_utils::prelude::*; use fstrings::f; @@ -210,3 +212,80 @@ fn local_and_global_scalars() { ]; assert_eq!(actual_trace, expected_trace); } + +#[test] +fn new_with_randomly_set_scalars_in_fold_1() { + let test_peer_id_1 = "test_peer_id_1"; + let mut test_vm_1 = create_avm(set_variable_call_service(json!([1, 2, 3])), test_peer_id_1); + + let test_peer_id_2 = "test_peer_id_2"; + let script = f!(r#" + (seq + (call "{test_peer_id_1}" ("" "") [] iterable) + (fold iterable iterator + (seq + (seq + (call "{test_peer_id_1}" ("" "") [] scalar) + (new scalar + (seq + (seq + (par + (call "{test_peer_id_2}" ("" "") [] scalar) + (null) + ) + (next iterator) + ) + (par + (call "{test_peer_id_2}" ("" "") [scalar]) + (null) + ) + ) + ) + ) + (call "{test_peer_id_1}" ("" "") [scalar]) + ) + ) + )"#); + + let result = call_vm!(test_vm_1, "", &script, "", ""); + assert_eq!(result.ret_code, 0) +} + +#[test] +fn new_with_randomly_set_scalars_in_fold_2() { + let test_peer_id_1 = "test_peer_id_1"; + let mut test_vm_1 = create_avm(set_variable_call_service(json!([1, 2, 3])), test_peer_id_1); + + let test_peer_id_2 = "test_peer_id_2"; + let variable_name = "scalar"; + let script = f!(r#" + (seq + (call "{test_peer_id_1}" ("" "") [] iterable) + (fold iterable iterator + (seq + (seq + (call "{test_peer_id_1}" ("" "") [] {variable_name}) + (new {variable_name} + (seq + (seq + (par + (call "{test_peer_id_2}" ("" "") [] {variable_name}) + (null) + ) + (next iterator) + ) + (call "{test_peer_id_1}" ("" "") [{variable_name}]) + ) + ) + ) + (call "{test_peer_id_1}" ("" "") [{variable_name}]) + ) + ) + )"#); + + let result = call_vm!(test_vm_1, "", &script, "", ""); + let expected_error = ExecutionError::Catchable(rc!(CatchableError::VariableWasNotInitializedAfterNew( + variable_name.to_string() + ))); + assert!(check_error(&result, expected_error)); +} diff --git a/air/tests/test_module/instructions/call.rs b/air/tests/test_module/instructions/call.rs index 5f34750d..95e7f05d 100644 --- a/air/tests/test_module/instructions/call.rs +++ b/air/tests/test_module/instructions/call.rs @@ -103,7 +103,7 @@ fn duplicate_variables() { let result = call_vm!(vm, "asd", script, "", ""); - let expected_error = UncatchableError::MultipleVariablesFound(variable_name.to_string()); + let expected_error = UncatchableError::ShadowingIsNotAllowed(variable_name.to_string()); assert!(check_error(&result, expected_error)); assert!(result.next_peer_pks.is_empty()); } diff --git a/air/tests/test_module/instructions/new.rs b/air/tests/test_module/instructions/new.rs index acec0714..7fc55209 100644 --- a/air/tests/test_module/instructions/new.rs +++ b/air/tests/test_module/instructions/new.rs @@ -271,3 +271,258 @@ fn new_with_errors() { }; assert_eq!(actual_global_streams, expected_global_streams); } + +#[test] +fn new_with_global_scalars() { + let set_variable_peer_id = "set_variable_peer_id"; + let variables_mapping = maplit::hashmap! { + "global".to_string() => json!(1), + "scoped".to_string() => json!(2), + }; + let mut set_variable_vm = create_avm( + set_variables_call_service(variables_mapping, VariableOptionSource::Argument(0)), + set_variable_peer_id, + ); + + let variable_receiver_peer_id = "variable_receiver_peer_id"; + let mut variable_receiver = create_avm(echo_call_service(), variable_receiver_peer_id); + + let script = f!(r#" + (seq + (seq + (call "{set_variable_peer_id}" ("" "") ["global"] scalar) + (new scalar + (seq + (call "{set_variable_peer_id}" ("" "") ["scoped"] scalar) + (call "{variable_receiver_peer_id}" ("" "") [scalar]) + ) + ) + ) + (call "{variable_receiver_peer_id}" ("" "") [scalar]) + )"#); + + let result = checked_call_vm!(set_variable_vm, "", &script, "", ""); + let result = checked_call_vm!(variable_receiver, "", &script, "", result.data); + let actual_trace = trace_from_result(&result); + + let expected_trace = vec![ + executed_state::scalar_number(1), + executed_state::scalar_number(2), + executed_state::scalar_number(2), + executed_state::scalar_number(1), + ]; + assert_eq!(actual_trace, expected_trace); +} + +const GET_ITERABLE_ACTION_NAME: &'static str = "get_iterable_action_name"; +const OUTSIDE_ACTION_NAME: &'static str = "outside_new"; +const INSIDE_ACTION_NAME: &'static str = "inside_new"; +const OUTPUT_ACTION_NAME: &'static str = "output"; + +fn prepare_new_test_call_service() -> CallServiceClosure { + let outside_new_id = std::cell::Cell::new(0u32); + let inside_new_id = std::cell::Cell::new(10u32); + + Box::new(move |mut params| { + let action = params.arguments.remove(0); + let action = action.as_str().unwrap(); + match action { + GET_ITERABLE_ACTION_NAME => CallServiceResult::ok(json!([1, 2, 3])), + OUTSIDE_ACTION_NAME => { + let outside_result = outside_new_id.get(); + outside_new_id.set(outside_result + 1); + CallServiceResult::ok(json!(outside_result)) + } + INSIDE_ACTION_NAME => { + let inside_result = inside_new_id.get(); + inside_new_id.set(inside_result + 1); + CallServiceResult::ok(json!(inside_result)) + } + OUTPUT_ACTION_NAME => { + let second_argument = params.arguments.remove(0); + CallServiceResult::ok(second_argument) + } + action_name => { + println!("unknown action: {:?}", action_name); + CallServiceResult::err(1, json!("no such action")) + } + } + }) +} + +#[test] +fn new_with_scalars_in_lfold_with_outside_next() { + let test_peer_id = "test_peer_id"; + + let test_call_service = prepare_new_test_call_service(); + let mut test_vm = create_avm(test_call_service, test_peer_id); + + let script = f!(r#" + (seq + (call "{test_peer_id}" ("" "") ["{GET_ITERABLE_ACTION_NAME}"] iterable) + (fold iterable iterator + (seq + (seq + (seq + (call "{test_peer_id}" ("" "") ["{OUTSIDE_ACTION_NAME}"] scalar) + (call "{test_peer_id}" ("" "") ["{OUTPUT_ACTION_NAME}" scalar]) + ) + (new scalar + (seq + (call "{test_peer_id}" ("" "") ["{INSIDE_ACTION_NAME}"] scalar) + (call "{test_peer_id}" ("" "") ["{OUTPUT_ACTION_NAME}" scalar]) + ) + ) + ) + (seq + (next iterator) + (call "{test_peer_id}" ("" "") ["{OUTPUT_ACTION_NAME}" scalar]) + ) + ) + ) + ) + "#); + + let result = checked_call_vm!(test_vm, "", &script, "", ""); + let actual_trace = trace_from_result(&result); + + let expected_trace = vec![ + executed_state::scalar(json!([1, 2, 3])), + executed_state::scalar_number(0), + executed_state::scalar_number(0), + executed_state::scalar_number(10), + executed_state::scalar_number(10), + executed_state::scalar_number(1), + executed_state::scalar_number(1), + executed_state::scalar_number(11), + executed_state::scalar_number(11), + executed_state::scalar_number(2), + executed_state::scalar_number(2), + executed_state::scalar_number(12), + executed_state::scalar_number(12), + executed_state::scalar_number(2), + executed_state::scalar_number(1), + executed_state::scalar_number(0), + ]; + assert_eq!(actual_trace, expected_trace); +} + +#[test] +fn new_with_scalars_in_rfold_with_outside_next() { + let test_peer_id = "test_peer_id"; + + let test_call_service = prepare_new_test_call_service(); + let mut test_vm = create_avm(test_call_service, test_peer_id); + + let script = f!(r#" + (seq + (call "{test_peer_id}" ("" "") ["{GET_ITERABLE_ACTION_NAME}"] iterable) + (fold iterable iterator + (seq + (next iterator) + (seq + (seq + (call "{test_peer_id}" ("" "") ["{OUTSIDE_ACTION_NAME}"] scalar) + (call "{test_peer_id}" ("" "") ["{OUTPUT_ACTION_NAME}" scalar]) + ) + (seq + (new scalar + (seq + (call "{test_peer_id}" ("" "") ["{INSIDE_ACTION_NAME}"] scalar) + (call "{test_peer_id}" ("" "") ["{OUTPUT_ACTION_NAME}" scalar]) + ) + ) + (call "{test_peer_id}" ("" "") ["{OUTPUT_ACTION_NAME}" scalar]) + ) + ) + ) + ) + ) + "#); + + let result = checked_call_vm!(test_vm, "", &script, "", ""); + let actual_trace = trace_from_result(&result); + + let expected_trace = vec![ + executed_state::scalar(json!([1, 2, 3])), + executed_state::scalar_number(0), + executed_state::scalar_number(0), + executed_state::scalar_number(10), + executed_state::scalar_number(10), + executed_state::scalar_number(0), + executed_state::scalar_number(1), + executed_state::scalar_number(1), + executed_state::scalar_number(11), + executed_state::scalar_number(11), + executed_state::scalar_number(1), + executed_state::scalar_number(2), + executed_state::scalar_number(2), + executed_state::scalar_number(12), + executed_state::scalar_number(12), + executed_state::scalar_number(2), + ]; + assert_eq!(actual_trace, expected_trace); +} + +#[test] +fn new_with_scalars_in_fold_with_inside_next() { + let test_peer_id = "test_peer_id"; + + let test_call_service = prepare_new_test_call_service(); + let mut test_vm = create_avm(test_call_service, test_peer_id); + + let script = f!(r#" + (seq + (call "{test_peer_id}" ("" "") ["{GET_ITERABLE_ACTION_NAME}"] iterable) + (fold iterable iterator + (seq + (seq + (seq + (call "{test_peer_id}" ("" "") ["{OUTSIDE_ACTION_NAME}"] scalar) + (call "{test_peer_id}" ("" "") ["{OUTPUT_ACTION_NAME}" scalar]) + ) + (new scalar + (seq + (call "{test_peer_id}" ("" "") ["{INSIDE_ACTION_NAME}"] scalar) + (seq + (call "{test_peer_id}" ("" "") ["{OUTPUT_ACTION_NAME}" scalar]) + (seq + (next iterator) + (call "{test_peer_id}" ("" "") ["{OUTPUT_ACTION_NAME}" scalar]) + ) + ) + ) + ) + ) + (call "{test_peer_id}" ("" "") ["{OUTPUT_ACTION_NAME}" scalar]) + ) + ) + ) + "#); + + let result = checked_call_vm!(test_vm, "", &script, "", ""); + let actual_trace = trace_from_result(&result); + + let expected_trace = vec![ + executed_state::scalar(json!([1, 2, 3])), + executed_state::scalar_number(0), + executed_state::scalar_number(0), + executed_state::scalar_number(10), + executed_state::scalar_number(10), + executed_state::scalar_number(1), + executed_state::scalar_number(1), + executed_state::scalar_number(11), + executed_state::scalar_number(11), + executed_state::scalar_number(2), + executed_state::scalar_number(2), + executed_state::scalar_number(12), + executed_state::scalar_number(12), + executed_state::scalar_number(12), + executed_state::scalar_number(2), + executed_state::scalar_number(11), + executed_state::scalar_number(1), + executed_state::scalar_number(10), + executed_state::scalar_number(0), + ]; + assert_eq!(actual_trace, expected_trace); +} diff --git a/air/tests/test_module/instructions/xor.rs b/air/tests/test_module/instructions/xor.rs index 586732d3..de4a145e 100644 --- a/air/tests/test_module/instructions/xor.rs +++ b/air/tests/test_module/instructions/xor.rs @@ -95,7 +95,7 @@ fn xor_multiple_variables_found() { let result = call_vm!(set_variables_vm, "asd", &script, "", ""); - let expected_error = UncatchableError::MultipleVariablesFound(variable_name.to_string()); + let expected_error = UncatchableError::ShadowingIsNotAllowed(variable_name.to_string()); assert!(check_error(&result, expected_error)); } diff --git a/crates/air-lib/air-parser/src/parser/tests/new.rs b/crates/air-lib/air-parser/src/parser/tests/new.rs index 164baeb2..5a8f2dc1 100644 --- a/crates/air-lib/air-parser/src/parser/tests/new.rs +++ b/crates/air-lib/air-parser/src/parser/tests/new.rs @@ -70,7 +70,6 @@ fn iterators_cant_be_restricted() { let errors = validator.finalize(); assert_eq!(errors.len(), 1); - println!("{:?}", errors); let error = &errors[0].error; let parser_error = match error { ParseError::User { error } => error,