diff --git a/Cargo.lock b/Cargo.lock index b2a28641..051b8685 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,9 +291,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.7.1" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" [[package]] name = "byteorder" @@ -935,9 +935,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", ] @@ -1074,9 +1074,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" +checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce" [[package]] name = "lock_api" diff --git a/air/src/execution_step/air/ap.rs b/air/src/execution_step/air/ap.rs index 9a77eaac..56afbf8f 100644 --- a/air/src/execution_step/air/ap.rs +++ b/air/src/execution_step/air/ap.rs @@ -17,12 +17,11 @@ mod apply_to_arguments; mod utils; -use super::call::call_result_setter::set_scalar_result; use super::call::call_result_setter::set_stream_result; use super::ExecutionCtx; use super::ExecutionResult; use super::TraceHandler; -use crate::execution_step::air::ResolvedCallResult; +use crate::execution_step::air::ValueAggregate; use crate::execution_step::boxed_value::Variable; use crate::execution_step::utils::apply_lambda; use crate::trace_to_exec_err; @@ -69,11 +68,11 @@ impl<'i> super::ExecutableInstruction<'i> for Ap<'i> { fn save_result<'ctx>( ap_result_type: &AstVariable<'ctx>, merger_ap_result: &MergerApResult, - result: ResolvedCallResult, + result: ValueAggregate, exec_ctx: &mut ExecutionCtx<'ctx>, ) -> ExecutionResult<()> { match ap_result_type { - AstVariable::Scalar(name) => set_scalar_result(result, name, exec_ctx), + AstVariable::Scalar(name) => exec_ctx.scalars.set_value(*name, result).map(|_| ()), AstVariable::Stream(name) => { let generation = ap_result_to_generation(merger_ap_result); set_stream_result(result, generation, name.to_string(), exec_ctx).map(|_| ()) 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 735f2462..2984669c 100644 --- a/air/src/execution_step/air/ap/apply_to_arguments.rs +++ b/air/src/execution_step/air/ap/apply_to_arguments.rs @@ -21,7 +21,7 @@ pub(super) fn apply_to_arg( exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler, should_touch_trace: bool, -) -> ExecutionResult { +) -> ExecutionResult { let result = match argument { ApArgument::ScalarVariable(scalar_name) => apply_scalar(scalar_name, exec_ctx, trace_ctx, should_touch_trace)?, ApArgument::VariableWithLambda(vl) => apply_json_argument(vl, exec_ctx, trace_ctx)?, @@ -40,18 +40,14 @@ fn apply_scalar( exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler, should_touch_trace: bool, -) -> ExecutionResult { - use crate::execution_step::ExecutionError::VariableNotFound; - use crate::execution_step::Scalar; +) -> ExecutionResult { + use crate::execution_step::ScalarRef; - let scalar = exec_ctx - .scalars - .get(scalar_name) - .ok_or_else(|| VariableNotFound(scalar_name.to_string()))?; + let scalar = exec_ctx.scalars.get(scalar_name)?; let mut result = match scalar { - Scalar::JValueRef(result) => result.clone(), - Scalar::JValueFoldCursor(iterator) => { + ScalarRef::Value(result) => result.clone(), + ScalarRef::IterableValue(iterator) => { let result = iterator.iterable.peek().expect( "peek always return elements inside fold,\ this guaranteed by implementation of next and avoiding empty folds", @@ -67,24 +63,24 @@ fn apply_scalar( Ok(result) } -fn apply_const(value: impl Into, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler) -> ResolvedCallResult { +fn apply_const(value: impl Into, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler) -> ValueAggregate { let value = Rc::new(value.into()); let tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.init_peer_id.clone()); let tetraplet = Rc::new(RefCell::new(tetraplet)); - ResolvedCallResult::new(value, tetraplet, trace_ctx.trace_pos()) + ValueAggregate::new(value, tetraplet, trace_ctx.trace_pos()) } fn apply_last_error( error_path: &LastErrorPath, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler, -) -> ExecutionResult { +) -> ExecutionResult { let (value, mut tetraplets) = crate::execution_step::utils::prepare_last_error(error_path, exec_ctx)?; let value = Rc::new(value); let tetraplet = tetraplets.remove(0); - let result = ResolvedCallResult::new(value, tetraplet, trace_ctx.trace_pos()); + let result = ValueAggregate::new(value, tetraplet, trace_ctx.trace_pos()); Ok(result) } @@ -92,14 +88,14 @@ fn apply_json_argument( vl: &VariableWithLambda<'_>, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler, -) -> ExecutionResult { +) -> ExecutionResult { let variable = Variable::from_ast(&vl.variable); let (jvalue, mut tetraplets) = apply_lambda(variable, &vl.lambda, exec_ctx)?; let tetraplet = tetraplets .pop() .unwrap_or_else(|| Rc::new(RefCell::new(SecurityTetraplet::default()))); - let result = ResolvedCallResult::new(Rc::new(jvalue), tetraplet, trace_ctx.trace_pos()); + let result = ValueAggregate::new(Rc::new(jvalue), tetraplet, trace_ctx.trace_pos()); Ok(result) } diff --git a/air/src/execution_step/air/call/call_result_setter.rs b/air/src/execution_step/air/call/call_result_setter.rs index abee3a28..3ca58d94 100644 --- a/air/src/execution_step/air/call/call_result_setter.rs +++ b/air/src/execution_step/air/call/call_result_setter.rs @@ -15,13 +15,11 @@ */ use super::*; -use crate::exec_err; use crate::execution_step::execution_context::*; use crate::execution_step::AstVariable; use crate::execution_step::Generation; -use crate::execution_step::ResolvedCallResult; -use crate::execution_step::Scalar; use crate::execution_step::Stream; +use crate::execution_step::ValueAggregate; use air_interpreter_data::CallResult; use air_interpreter_data::Value; @@ -34,14 +32,14 @@ use std::collections::hash_map::Entry::{Occupied, Vacant}; /// Writes result of a local `Call` instruction to `ExecutionCtx` at `output`. /// Returns call result. pub(crate) fn set_local_result<'i>( - executed_result: ResolvedCallResult, + executed_result: ValueAggregate, output: &CallOutputValue<'i>, exec_ctx: &mut ExecutionCtx<'i>, ) -> ExecutionResult { let result_value = executed_result.result.clone(); match output { CallOutputValue::Variable(AstVariable::Scalar(name)) => { - set_scalar_result(executed_result, name, exec_ctx)?; + exec_ctx.scalars.set_value(*name, executed_result)?; Ok(CallResult::executed_scalar(result_value)) } CallOutputValue::Variable(AstVariable::Stream(name)) => { @@ -67,11 +65,11 @@ pub(crate) fn set_result_from_value<'i>( ) -> ExecutionResult<()> { match (output, value) { (CallOutputValue::Variable(AstVariable::Scalar(name)), Value::Scalar(value)) => { - let result = ResolvedCallResult::new(value, tetraplet, trace_pos); - set_scalar_result(result, name, exec_ctx)?; + let result = ValueAggregate::new(value, tetraplet, trace_pos); + exec_ctx.scalars.set_value(*name, result)?; } (CallOutputValue::Variable(AstVariable::Stream(name)), Value::Stream { value, generation }) => { - let result = ResolvedCallResult::new(value, tetraplet, trace_pos); + let result = ValueAggregate::new(value, tetraplet, trace_pos); let generation = Generation::Nth(generation); let _ = set_stream_result(result, generation, name.to_string(), exec_ctx)?; } @@ -83,68 +81,9 @@ pub(crate) fn set_result_from_value<'i>( Ok(()) } -#[macro_export] -macro_rules! shadowing_allowed( - ($exec_ctx:ident, $entry:ident) => { { - // check that current execution_step flow is inside a fold block - if $exec_ctx.met_folds.is_empty() { - // shadowing is allowed only inside fold blocks - return exec_err!(ExecutionError::MultipleVariablesFound($entry.key().clone())); - } - - match $entry.get() { - Scalar::JValueRef(_) => {} - // shadowing is allowed only for JValue not iterable - _ => return exec_err!(ExecutionError::IterableShadowing($entry.key().clone())), - }; - - ExecutionResult::Ok(()) - }} -); - -// TODO: decouple this function to a separate module -pub(crate) fn set_scalar_result<'i>( - executed_result: ResolvedCallResult, - scalar_name: &'i str, - exec_ctx: &mut ExecutionCtx<'i>, -) -> ExecutionResult<()> { - meet_scalar(scalar_name, executed_result.clone(), exec_ctx)?; - - match exec_ctx.scalars.entry(scalar_name.to_string()) { - Vacant(entry) => { - entry.insert(Scalar::JValueRef(executed_result)); - } - Occupied(mut entry) => { - // the macro instead of a function because of borrowing - shadowing_allowed!(exec_ctx, entry)?; - entry.insert(Scalar::JValueRef(executed_result)); - } - }; - - Ok(()) -} - -/// Inserts meet variable name into met calls in fold to allow shadowing. -fn meet_scalar<'i>( - scalar_name: &'i str, - executed_result: ResolvedCallResult, - exec_ctx: &mut ExecutionCtx<'i>, -) -> ExecutionResult<()> { - if let Some(fold_block_name) = exec_ctx.met_folds.back() { - let fold_state = match exec_ctx.scalars.get_mut(*fold_block_name) { - Some(Scalar::JValueFoldCursor(fold_state)) => fold_state, - _ => unreachable!("fold block data must be represented as fold cursor"), - }; - - fold_state.met_variables.insert(scalar_name, executed_result); - } - - Ok(()) -} - // TODO: decouple this function to a separate module pub(crate) fn set_stream_result( - executed_result: ResolvedCallResult, + executed_result: ValueAggregate, generation: Generation, stream_name: String, exec_ctx: &mut ExecutionCtx<'_>, diff --git a/air/src/execution_step/air/call/prev_result_handler.rs b/air/src/execution_step/air/call/prev_result_handler.rs index 5985d1d3..7e55261a 100644 --- a/air/src/execution_step/air/call/prev_result_handler.rs +++ b/air/src/execution_step/air/call/prev_result_handler.rs @@ -85,7 +85,7 @@ pub(super) fn handle_prev_state<'i>( } use super::call_result_setter::*; -use crate::execution_step::ResolvedCallResult; +use crate::execution_step::ValueAggregate; use crate::JValue; fn update_state_with_service_result<'i>( @@ -102,7 +102,7 @@ fn update_state_with_service_result<'i>( let trace_pos = trace_ctx.trace_pos(); - let executed_result = ResolvedCallResult::new(result, tetraplet.clone(), trace_pos); + let executed_result = ValueAggregate::new(result, tetraplet.clone(), trace_pos); let new_call_result = set_local_result(executed_result, output, exec_ctx)?; trace_ctx.meet_call_end(new_call_result); diff --git a/air/src/execution_step/air/call/resolved_call.rs b/air/src/execution_step/air/call/resolved_call.rs index 568509e6..d3967413 100644 --- a/air/src/execution_step/air/call/resolved_call.rs +++ b/air/src/execution_step/air/call/resolved_call.rs @@ -169,7 +169,7 @@ impl<'i> ResolvedCall<'i> { /// Check output type name for being already in execution context. // TODO: this check should be moved on a parsing stage fn check_output_name(output: &CallOutputValue<'_>, exec_ctx: &ExecutionCtx<'_>) -> ExecutionResult<()> { - use crate::execution_step::boxed_value::Scalar; + use crate::execution_step::boxed_value::ScalarRef; let scalar_name = match output { CallOutputValue::Variable(AstVariable::Scalar(name)) => *name, @@ -177,15 +177,14 @@ fn check_output_name(output: &CallOutputValue<'_>, exec_ctx: &ExecutionCtx<'_>) }; match exec_ctx.scalars.get(scalar_name) { - Some(Scalar::JValueRef(_)) => { - if exec_ctx.met_folds.is_empty() { - // shadowing is allowed only inside fold blocks - crate::exec_err!(ExecutionError::MultipleVariablesFound(scalar_name.to_string())) - } else { + Ok(ScalarRef::Value(_)) => { + if exec_ctx.scalars.shadowing_allowed() { Ok(()) + } else { + crate::exec_err!(ExecutionError::MultipleVariablesFound(scalar_name.to_string())) } } - Some(_) => crate::exec_err!(ExecutionError::IterableShadowing(scalar_name.to_string())), - None => Ok(()), + Ok(ScalarRef::IterableValue(_)) => crate::exec_err!(ExecutionError::IterableShadowing(scalar_name.to_string())), + Err(_) => Ok(()), } } diff --git a/air/src/execution_step/air/fold/fold_state.rs b/air/src/execution_step/air/fold/fold_state.rs index b049b532..9f07393d 100644 --- a/air/src/execution_step/air/fold/fold_state.rs +++ b/air/src/execution_step/air/fold/fold_state.rs @@ -16,17 +16,13 @@ use super::Instruction; use super::IterableValue; -use super::ResolvedCallResult; -use std::collections::HashMap; use std::rc::Rc; pub(crate) struct FoldState<'i> { pub(crate) iterable: IterableValue, pub(crate) iterable_type: IterableType, pub(crate) instr_head: Rc>, - // map of met variables inside this (not any inner) fold block with their initial values - pub(crate) met_variables: HashMap<&'i str, ResolvedCallResult>, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -45,7 +41,6 @@ impl<'i> FoldState<'i> { iterable, iterable_type, instr_head, - met_variables: HashMap::new(), } } } diff --git a/air/src/execution_step/air/fold/mod.rs b/air/src/execution_step/air/fold/mod.rs index 7fb4f242..457ae848 100644 --- a/air/src/execution_step/air/fold/mod.rs +++ b/air/src/execution_step/air/fold/mod.rs @@ -16,17 +16,15 @@ mod fold_state; mod utils; -mod variable_handler; pub(crate) use fold_state::FoldState; pub(crate) use fold_state::IterableType; pub(super) use utils::*; -pub(super) use variable_handler::VariableHandler; use super::ExecutionCtx; use super::ExecutionError; use super::ExecutionResult; use super::Instruction; -use super::ResolvedCallResult; -use super::Scalar; +use super::ScalarRef; +use super::ValueAggregate; use crate::execution_step::boxed_value::*; diff --git a/air/src/execution_step/air/fold/utils.rs b/air/src/execution_step/air/fold/utils.rs index 469b4f28..326a0f6b 100644 --- a/air/src/execution_step/air/fold/utils.rs +++ b/air/src/execution_step/air/fold/utils.rs @@ -88,19 +88,18 @@ fn create_scalar_iterable<'ctx>( exec_ctx: &ExecutionCtx<'ctx>, variable_name: &str, ) -> ExecutionResult { - match exec_ctx.scalars.get(variable_name) { - Some(Scalar::JValueRef(call_result)) => from_call_result(call_result.clone()), - Some(Scalar::JValueFoldCursor(fold_state)) => { + match exec_ctx.scalars.get(variable_name)? { + ScalarRef::Value(call_result) => from_call_result(call_result.clone()), + ScalarRef::IterableValue(fold_state) => { let iterable_value = fold_state.iterable.peek().unwrap(); let call_result = iterable_value.into_resolved_result(); from_call_result(call_result) } - _ => return exec_err!(ExecutionError::VariableNotFound(variable_name.to_string())), } } /// Constructs iterable value from resolved call result. -fn from_call_result(call_result: ResolvedCallResult) -> ExecutionResult { +fn from_call_result(call_result: ValueAggregate) -> ExecutionResult { use ExecutionError::IncompatibleJValueType; let len = match &call_result.result.deref() { @@ -128,19 +127,18 @@ fn create_scalar_lambda_iterable<'ctx>( ) -> ExecutionResult { use crate::execution_step::lambda_applier::select; - match exec_ctx.scalars.get(scalar_name) { - Some(Scalar::JValueRef(variable)) => { + match exec_ctx.scalars.get(scalar_name)? { + ScalarRef::Value(variable) => { let jvalues = select(&variable.result, lambda.iter())?; from_jvalue(jvalues, variable.tetraplet.clone(), lambda) } - Some(Scalar::JValueFoldCursor(fold_state)) => { + ScalarRef::IterableValue(fold_state) => { let iterable_value = fold_state.iterable.peek().unwrap(); let jvalues = iterable_value.apply_lambda(lambda)?; let tetraplet = as_tetraplet(&iterable_value); from_jvalue(jvalues[0], tetraplet, lambda) } - _ => return exec_err!(ExecutionError::VariableNotFound(scalar_name.to_string())), } } diff --git a/air/src/execution_step/air/fold/variable_handler.rs b/air/src/execution_step/air/fold/variable_handler.rs deleted file mode 100644 index db9eed33..00000000 --- a/air/src/execution_step/air/fold/variable_handler.rs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2021 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::ExecutionCtx; -use super::ExecutionResult; -use super::FoldState; -use super::Scalar; - -use std::collections::HashMap; - -pub(crate) struct VariableHandler<'i> { - iterator: &'i str, -} - -impl<'i> VariableHandler<'i> { - pub(crate) fn init<'ctx: 'i>( - exec_ctx: &mut ExecutionCtx<'ctx>, - iterator: &'ctx str, - fold_state: FoldState<'ctx>, - ) -> ExecutionResult { - Self::try_insert_fold_state(exec_ctx, iterator, fold_state)?; - Self::meet_iterator(exec_ctx, iterator); - - let handler = Self { iterator }; - Ok(handler) - } - - pub(crate) fn cleanup(self, exec_ctx: &mut ExecutionCtx<'_>) { - let fold_state = match exec_ctx.scalars.remove(self.iterator) { - Some(Scalar::JValueFoldCursor(fold_state)) => fold_state, - _ => unreachable!("fold cursor is changed only inside fold block"), - }; - - for (variable_name, _) in fold_state.met_variables { - exec_ctx.scalars.remove(variable_name); - } - exec_ctx.met_folds.pop_back(); - - // TODO: fix 3 or more inner folds behaviour - if let Some(fold_block_name) = exec_ctx.met_folds.back() { - let fold_state = match exec_ctx.scalars.get(*fold_block_name) { - Some(Scalar::JValueFoldCursor(fold_state)) => fold_state, - _ => unreachable!("fold block data must be represented as fold cursor"), - }; - - let mut upper_fold_values = HashMap::new(); - for (variable_name, variable) in fold_state.met_variables.iter() { - upper_fold_values.insert(variable_name.to_string(), Scalar::JValueRef(variable.clone())); - } - - exec_ctx.scalars.extend(upper_fold_values); - } - } - - fn try_insert_fold_state<'ctx>( - exec_ctx: &mut ExecutionCtx<'ctx>, - iterator: &'ctx str, - fold_state: FoldState<'ctx>, - ) -> ExecutionResult<()> { - use super::ExecutionError::MultipleFoldStates; - - let previous_value = exec_ctx - .scalars - .insert(iterator.to_string(), Scalar::JValueFoldCursor(fold_state)); - - if previous_value.is_some() { - return crate::exec_err!(MultipleFoldStates(iterator.to_string())); - } - - Ok(()) - } - - fn meet_iterator<'ctx>(exec_ctx: &mut ExecutionCtx<'ctx>, iterator: &'ctx str) { - exec_ctx.met_folds.push_back(iterator); - } -} diff --git a/air/src/execution_step/air/fold_scalar.rs b/air/src/execution_step/air/fold_scalar.rs index 00872afd..f9b661b2 100644 --- a/air/src/execution_step/air/fold_scalar.rs +++ b/air/src/execution_step/air/fold_scalar.rs @@ -29,7 +29,9 @@ impl<'i> ExecutableInstruction<'i> for FoldScalar<'i> { fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { log_instruction!(fold, exec_ctx, trace_ctx); - match construct_scalar_iterable_value(&self.iterable, exec_ctx)? { + exec_ctx.scalars.meet_fold_begin(); + + let fold_result = match construct_scalar_iterable_value(&self.iterable, exec_ctx)? { FoldIterableScalar::Empty => Ok(()), FoldIterableScalar::Scalar(iterable) => fold( iterable, @@ -39,7 +41,11 @@ impl<'i> ExecutableInstruction<'i> for FoldScalar<'i> { exec_ctx, trace_ctx, ), - } + }; + + exec_ctx.scalars.meet_fold_end(); + + fold_result } } @@ -52,11 +58,11 @@ pub(super) fn fold<'i>( trace_ctx: &mut TraceHandler, ) -> ExecutionResult<()> { let fold_state = FoldState::from_iterable(iterable, iterable_type, instruction.clone()); - let variable_handler = VariableHandler::init(exec_ctx, iterator, fold_state)?; + exec_ctx.scalars.set_iterable_value(iterator, fold_state)?; instruction.execute(exec_ctx, trace_ctx)?; - variable_handler.cleanup(exec_ctx); + exec_ctx.scalars.remove_iterable_value(iterator); Ok(()) } diff --git a/air/src/execution_step/air/fold_stream.rs b/air/src/execution_step/air/fold_stream.rs index 5ab9740c..1623533e 100644 --- a/air/src/execution_step/air/fold_stream.rs +++ b/air/src/execution_step/air/fold_stream.rs @@ -36,6 +36,7 @@ impl<'i> ExecutableInstruction<'i> for FoldStream<'i> { let fold_id = exec_ctx.tracker.fold.seen_stream_count; trace_to_exec_err!(trace_ctx.meet_fold_start(fold_id))?; + exec_ctx.scalars.meet_fold_begin(); for iterable in iterables { let value = match iterable.peek() { @@ -63,6 +64,7 @@ impl<'i> ExecutableInstruction<'i> for FoldStream<'i> { } trace_to_exec_err!(trace_ctx.meet_fold_end(fold_id))?; + exec_ctx.scalars.meet_fold_end(); Ok(()) } diff --git a/air/src/execution_step/air/mod.rs b/air/src/execution_step/air/mod.rs index 20326494..40e8df71 100644 --- a/air/src/execution_step/air/mod.rs +++ b/air/src/execution_step/air/mod.rs @@ -30,8 +30,8 @@ mod xor; pub(crate) use fold::FoldState; -use super::boxed_value::ResolvedCallResult; -use super::boxed_value::Scalar; +use super::boxed_value::ScalarRef; +use super::boxed_value::ValueAggregate; use super::execution_context::*; use super::Catchable; use super::ExecutionCtx; @@ -147,12 +147,8 @@ macro_rules! log_instruction { log::debug!(target: air_log_targets::INSTRUCTION, "> {}", stringify!($instr_name)); let mut variables = String::from(" scalars:"); - if $exec_ctx.scalars.is_empty() { - variables.push_str(" empty"); - } - for (key, value) in $exec_ctx.scalars.iter() { - variables.push_str(&format!("\n {} => {}", key, value)); - } + + variables.push_str(&format!("\n {}", $exec_ctx.scalars)); variables.push_str(" streams:"); if $exec_ctx.streams.is_empty() { diff --git a/air/src/execution_step/air/next.rs b/air/src/execution_step/air/next.rs index 77ecd6ef..24667348 100644 --- a/air/src/execution_step/air/next.rs +++ b/air/src/execution_step/air/next.rs @@ -16,12 +16,9 @@ use super::fold::IterableType; use super::ExecutionCtx; -use super::ExecutionError; use super::ExecutionResult; use super::FoldState; -use super::Scalar; use super::TraceHandler; -use crate::exec_err; use crate::log_instruction; use crate::trace_to_exec_err; @@ -32,7 +29,7 @@ impl<'i> super::ExecutableInstruction<'i> for Next<'i> { log_instruction!(next, exec_ctx, trace_ctx); let iterator_name = self.0; - let fold_state = try_get_fold_state(exec_ctx, iterator_name)?; + let fold_state = exec_ctx.scalars.get_iterable_mut(iterator_name)?; maybe_meet_iteration_end(fold_state, trace_ctx)?; if !fold_state.iterable.next() { @@ -48,46 +45,14 @@ impl<'i> super::ExecutableInstruction<'i> for Next<'i> { next_instr.execute(exec_ctx, trace_ctx)?; // get the same fold state again because of borrow checker - match exec_ctx.scalars.get_mut(iterator_name) { - // move iterator back to provide correct value for possible subtree after next - // (for example for cases such as right fold) - Some(Scalar::JValueFoldCursor(fold_state)) => fold_state.iterable.prev(), - _ => unreachable!("iterator value shouldn't changed inside fold"), - }; - - // get this fold state the second time to bypass borrow checker - let fold_state = try_get_fold_state(exec_ctx, iterator_name)?; + let fold_state = exec_ctx.scalars.get_iterable_mut(iterator_name)?; + fold_state.iterable.prev(); maybe_meet_back_iterator(fold_state, trace_ctx)?; Ok(()) } } -fn try_get_fold_state<'i, 'ctx>( - exec_ctx: &'ctx mut ExecutionCtx<'i>, - iterator_name: &str, -) -> ExecutionResult<&'ctx mut FoldState<'i>> { - use ExecutionError::FoldStateNotFound; - use ExecutionError::IncompatibleAValueType; - - let avalue = exec_ctx - .scalars - .get_mut(iterator_name) - .ok_or_else(|| FoldStateNotFound(iterator_name.to_string()))?; - - match avalue { - Scalar::JValueFoldCursor(state) => Ok(state), - v => { - // it's not possible to use unreachable here - // because at now next syntactically could be used without fold - exec_err!(IncompatibleAValueType( - format!("{}", v), - String::from("JValueFoldCursor"), - )) - } - } -} - fn maybe_meet_iteration_start(fold_state: &FoldState<'_>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { if let IterableType::Stream(fold_id) = &fold_state.iterable_type { trace_to_exec_err!(trace_ctx.meet_iteration_start(*fold_id, fold_state.iterable.peek().unwrap().pos()))?; diff --git a/air/src/execution_step/boxed_value/iterable.rs b/air/src/execution_step/boxed_value/iterable.rs index c895464d..ca697e4b 100644 --- a/air/src/execution_step/boxed_value/iterable.rs +++ b/air/src/execution_step/boxed_value/iterable.rs @@ -23,7 +23,7 @@ pub(crate) use json_path_result::IterableLambdaResult; pub(crate) use resolved_call::IterableResolvedCall; pub(crate) use vec_resolved_call::IterableVecResolvedCall; -use super::ResolvedCallResult; +use super::ValueAggregate; use crate::execution_step::RSecurityTetraplet; use crate::JValue; @@ -74,7 +74,7 @@ impl IterableItem<'_> { *pos } - pub(crate) fn into_resolved_result(self) -> ResolvedCallResult { + pub(crate) fn into_resolved_result(self) -> ValueAggregate { use IterableItem::*; let (value, tetraplet, pos) = match self { @@ -83,7 +83,7 @@ impl IterableItem<'_> { RcValue(ingredients) => ingredients, }; - ResolvedCallResult::new(value, tetraplet, pos) + ValueAggregate::new(value, tetraplet, pos) } } diff --git a/air/src/execution_step/boxed_value/iterable/resolved_call.rs b/air/src/execution_step/boxed_value/iterable/resolved_call.rs index aca17abb..6776de9b 100644 --- a/air/src/execution_step/boxed_value/iterable/resolved_call.rs +++ b/air/src/execution_step/boxed_value/iterable/resolved_call.rs @@ -16,7 +16,7 @@ use super::Iterable; use super::IterableItem; -use super::ResolvedCallResult; +use super::ValueAggregate; use crate::foldable_next; use crate::foldable_prev; use crate::JValue; @@ -26,13 +26,13 @@ use std::ops::Deref; /// Used for iterating over JValue of array type. #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct IterableResolvedCall { - pub(crate) call_result: ResolvedCallResult, + pub(crate) call_result: ValueAggregate, pub(crate) cursor: usize, pub(crate) len: usize, } impl IterableResolvedCall { - pub(crate) fn init(call_result: ResolvedCallResult, len: usize) -> Self { + pub(crate) fn init(call_result: ValueAggregate, len: usize) -> Self { Self { call_result, cursor: 0, @@ -57,7 +57,7 @@ impl<'ctx> Iterable<'ctx> for IterableResolvedCall { return None; } - let ResolvedCallResult { + let ValueAggregate { result, tetraplet, trace_pos, diff --git a/air/src/execution_step/boxed_value/iterable/vec_resolved_call.rs b/air/src/execution_step/boxed_value/iterable/vec_resolved_call.rs index 5c3a47de..d2d5cfc8 100644 --- a/air/src/execution_step/boxed_value/iterable/vec_resolved_call.rs +++ b/air/src/execution_step/boxed_value/iterable/vec_resolved_call.rs @@ -16,19 +16,19 @@ use super::Iterable; use super::IterableItem; -use super::ResolvedCallResult; +use super::ValueAggregate; use crate::foldable_next; use crate::foldable_prev; /// Used for iterating over stream with JValues. #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct IterableVecResolvedCall { - pub(crate) call_results: Vec, + pub(crate) call_results: Vec, pub(crate) cursor: usize, } impl IterableVecResolvedCall { - pub(crate) fn init(call_results: Vec) -> Self { + pub(crate) fn init(call_results: Vec) -> Self { Self { call_results, cursor: 0, @@ -52,7 +52,7 @@ impl<'ctx> Iterable<'ctx> for IterableVecResolvedCall { return None; } - let ResolvedCallResult { + let ValueAggregate { result, tetraplet, trace_pos, diff --git a/air/src/execution_step/boxed_value/jvaluable.rs b/air/src/execution_step/boxed_value/jvaluable.rs index 27c4e5f4..35758a97 100644 --- a/air/src/execution_step/boxed_value/jvaluable.rs +++ b/air/src/execution_step/boxed_value/jvaluable.rs @@ -23,7 +23,7 @@ mod stream; use super::iterable::IterableItem; use super::ExecutionError; use super::ExecutionResult; -use super::ResolvedCallResult; +use super::ValueAggregate; use crate::execution_step::lambda_applier::*; use crate::execution_step::SecurityTetraplets; use crate::JValue; diff --git a/air/src/execution_step/boxed_value/jvaluable/cell_vec_resolved_call_result.rs b/air/src/execution_step/boxed_value/jvaluable/cell_vec_resolved_call_result.rs index 1a810412..08adc9d5 100644 --- a/air/src/execution_step/boxed_value/jvaluable/cell_vec_resolved_call_result.rs +++ b/air/src/execution_step/boxed_value/jvaluable/cell_vec_resolved_call_result.rs @@ -17,7 +17,7 @@ use super::select_from_stream; use super::ExecutionResult; use super::JValuable; -use super::ResolvedCallResult; +use super::ValueAggregate; use crate::execution_step::SecurityTetraplets; use crate::JValue; use crate::LambdaAST; @@ -27,7 +27,7 @@ use air_lambda_ast::format_ast; use std::borrow::Cow; use std::ops::Deref; -impl JValuable for std::cell::Ref<'_, Vec> { +impl JValuable for std::cell::Ref<'_, Vec> { fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult> { let stream_iter = self.iter().map(|r| r.result.deref()); let select_result = select_from_stream(stream_iter, lambda)?; diff --git a/air/src/execution_step/boxed_value/jvaluable/resolved_call_result.rs b/air/src/execution_step/boxed_value/jvaluable/resolved_call_result.rs index c8ba81ef..4f1bc115 100644 --- a/air/src/execution_step/boxed_value/jvaluable/resolved_call_result.rs +++ b/air/src/execution_step/boxed_value/jvaluable/resolved_call_result.rs @@ -18,7 +18,7 @@ use super::select; use super::ExecutionResult; use super::JValuable; use super::LambdaAST; -use super::ResolvedCallResult; +use super::ValueAggregate; use crate::execution_step::SecurityTetraplets; use crate::JValue; @@ -27,7 +27,7 @@ use air_lambda_ast::format_ast; use std::borrow::Cow; use std::ops::Deref; -impl JValuable for ResolvedCallResult { +impl JValuable for ValueAggregate { fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult> { let selected_value = select(&self.result, lambda.iter())?; Ok(vec![selected_value]) diff --git a/air/src/execution_step/boxed_value/mod.rs b/air/src/execution_step/boxed_value/mod.rs index 1308c9e2..5058cfbd 100644 --- a/air/src/execution_step/boxed_value/mod.rs +++ b/air/src/execution_step/boxed_value/mod.rs @@ -23,8 +23,8 @@ mod variable; pub(crate) use super::ExecutionError; pub(crate) use iterable::*; pub(crate) use jvaluable::*; -pub(crate) use scalar::ResolvedCallResult; -pub(crate) use scalar::Scalar; +pub(crate) use scalar::ScalarRef; +pub(crate) use scalar::ValueAggregate; pub(crate) use stream::Generation; pub(crate) use stream::Stream; pub(crate) use stream::StreamIter; diff --git a/air/src/execution_step/boxed_value/scalar.rs b/air/src/execution_step/boxed_value/scalar.rs index 899e671a..9d95482f 100644 --- a/air/src/execution_step/boxed_value/scalar.rs +++ b/air/src/execution_step/boxed_value/scalar.rs @@ -27,22 +27,22 @@ use std::fmt::Formatter; use std::rc::Rc; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct ResolvedCallResult { +pub struct ValueAggregate { pub result: Rc, pub tetraplet: RSecurityTetraplet, pub trace_pos: usize, } -pub(crate) enum Scalar<'i> { - JValueRef(ResolvedCallResult), - JValueFoldCursor(FoldState<'i>), +pub(crate) enum ScalarRef<'i> { + Value(&'i ValueAggregate), + IterableValue(&'i FoldState<'i>), } -impl<'i> Scalar<'i> { - pub(crate) fn to_jvaluable<'ctx>(&'ctx self) -> Box { +impl<'i> ScalarRef<'i> { + pub(crate) fn into_jvaluable(self) -> Box { match self { - Scalar::JValueRef(value) => Box::new(value.clone()), - Scalar::JValueFoldCursor(fold_state) => { + ScalarRef::Value(value) => Box::new(value.clone()), + ScalarRef::IterableValue(fold_state) => { let peeked_value = fold_state.iterable.peek().unwrap(); Box::new(peeked_value) } @@ -50,7 +50,7 @@ impl<'i> Scalar<'i> { } } -impl ResolvedCallResult { +impl ValueAggregate { pub(crate) fn new(result: Rc, tetraplet: RSecurityTetraplet, trace_pos: usize) -> Self { Self { result, @@ -60,11 +60,11 @@ impl ResolvedCallResult { } } -impl<'i> Display for Scalar<'i> { +impl<'i> Display for ScalarRef<'i> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - Scalar::JValueRef(value) => write!(f, "{:?}", value)?, - Scalar::JValueFoldCursor(cursor) => { + ScalarRef::Value(value) => write!(f, "{:?}", value)?, + ScalarRef::IterableValue(cursor) => { let iterable = &cursor.iterable; write!(f, "cursor, current value: {:?}", iterable.peek())?; } diff --git a/air/src/execution_step/boxed_value/stream.rs b/air/src/execution_step/boxed_value/stream.rs index b5ce985b..cac18a49 100644 --- a/air/src/execution_step/boxed_value/stream.rs +++ b/air/src/execution_step/boxed_value/stream.rs @@ -16,7 +16,7 @@ use super::ExecutionError; use super::ExecutionResult; -use super::ResolvedCallResult; +use super::ValueAggregate; use crate::exec_err; use crate::JValue; @@ -31,20 +31,20 @@ use std::fmt::Formatter; /// obtained values from a current_data that were not present in prev_data becomes a new generation. // TODO: make it non-pub after boxed value refactoring. #[derive(Debug, Default, Clone)] -pub(crate) struct Stream(Vec>); +pub(crate) struct Stream(Vec>); impl Stream { pub(crate) fn from_generations_count(count: usize) -> Self { Self(vec![vec![]; count + 1]) } - pub(crate) fn from_value(value: ResolvedCallResult) -> Self { + pub(crate) fn from_value(value: ValueAggregate) -> Self { Self(vec![vec![value]]) } // if generation is None, value would be added to the last generation, otherwise it would // be added to given generation - pub(crate) fn add_value(&mut self, value: ResolvedCallResult, generation: Generation) -> ExecutionResult { + pub(crate) fn add_value(&mut self, value: ValueAggregate, generation: Generation) -> ExecutionResult { let generation = match generation { Generation::Last => self.0.len() - 1, Generation::Nth(id) => id as usize, @@ -95,7 +95,7 @@ impl Stream { } pub(crate) fn iter(&self, generation: Generation) -> Option> { - let iter: Box> = match generation { + let iter: Box> = match generation { Generation::Nth(generation) if generation as usize >= self.generations_count() => return None, Generation::Nth(generation) => Box::new(self.0.iter().take(generation as usize + 1).flat_map(|v| v.iter())), Generation::Last => Box::new(self.0.iter().flat_map(|v| v.iter())), @@ -109,7 +109,7 @@ impl Stream { } pub(crate) fn slice_iter(&self, generation: Generation) -> Option> { - let iter: Box> = match generation { + let iter: Box> = match generation { Generation::Nth(generation) if generation as usize >= self.generations_count() => return None, Generation::Nth(generation) => Box::new(self.0.iter().take(generation as usize + 1).map(|v| v.as_slice())), Generation::Last => Box::new(self.0.iter().map(|v| v.as_slice())), @@ -142,12 +142,12 @@ impl Generation { } pub(crate) struct StreamIter<'result> { - iter: Box + 'result>, + iter: Box + 'result>, len: usize, } impl<'result> Iterator for StreamIter<'result> { - type Item = &'result ResolvedCallResult; + type Item = &'result ValueAggregate; fn next(&mut self) -> Option { if self.len > 0 { @@ -164,12 +164,12 @@ impl<'result> Iterator for StreamIter<'result> { impl<'result> ExactSizeIterator for StreamIter<'result> {} pub(crate) struct StreamSliceIter<'slice> { - iter: Box + 'slice>, + iter: Box + 'slice>, len: usize, } impl<'slice> Iterator for StreamSliceIter<'slice> { - type Item = &'slice [ResolvedCallResult]; + type Item = &'slice [ValueAggregate]; fn next(&mut self) -> Option { if self.len > 0 { diff --git a/air/src/execution_step/errors.rs b/air/src/execution_step/errors.rs index 364295ef..d59d112a 100644 --- a/air/src/execution_step/errors.rs +++ b/air/src/execution_step/errors.rs @@ -62,21 +62,17 @@ pub(crate) enum ExecutionError { #[error("lambda is applied to an empty stream")] EmptyStreamLambdaError, - /// Provided JValue has incompatible with target type. + /// Provided JValue has incompatible type with a requested one. #[error("expected JValue type '{1}', but got '{0}' JValue")] IncompatibleJValueType(JValue, &'static str), - /// Provided AValue has incompatible with target type. - #[error("expected AValue type '{1}', but got '{0}' AValue")] - IncompatibleAValueType(String, String), - /// Fold state wasn't found for such iterator name. #[error("fold state not found for this iterable '{0}'")] FoldStateNotFound(String), /// Multiple fold states found for such iterator name. - #[error("multiple fold states found for iterable '{0}'")] - MultipleFoldStates(String), + #[error("multiple iterable values found for iterable name '{0}'")] + MultipleIterableValues(String), /// A fold instruction must iterate over array value. #[error("lambda '{1}' returned non-array value '{0}' for fold instruction")] diff --git a/air/src/execution_step/execution_context/context.rs b/air/src/execution_step/execution_context/context.rs index fe61b95a..827968e1 100644 --- a/air/src/execution_step/execution_context/context.rs +++ b/air/src/execution_step/execution_context/context.rs @@ -16,7 +16,7 @@ use super::LastErrorDescriptor; use super::LastErrorWithTetraplet; -use crate::execution_step::boxed_value::Scalar; +use super::Scalars; use crate::execution_step::boxed_value::Stream; use air_execution_info_collector::InstructionTracker; @@ -24,36 +24,34 @@ use air_interpreter_interface::*; use std::cell::RefCell; use std::collections::HashMap; -use std::collections::VecDeque; use std::rc::Rc; /// Contains all necessary state needed to execute AIR script. #[derive(Default)] pub(crate) struct ExecutionCtx<'i> { /// Contains all scalars. - // TODO: use shared string (Rc) to avoid copying. - pub scalars: HashMap>, + pub(crate) scalars: Scalars<'i>, /// Contains all streams. // TODO: use shared string (Rc) to avoid copying. - pub streams: HashMap>, + pub(crate) streams: HashMap>, /// Set of peer public keys that should receive resulted data. - pub next_peer_pks: Vec, + pub(crate) next_peer_pks: Vec, /// PeerId of a peer executing this AIR script at the moment. - pub current_peer_id: Rc, + pub(crate) current_peer_id: Rc, /// PeerId of a peer send this AIR script. - pub init_peer_id: String, + pub(crate) init_peer_id: String, /// Last error produced by local service. /// None means that there weren't any error. - pub last_error: Option, + pub(crate) last_error: Option, /// True, if last error could be set. This flag is used to distinguish /// whether an error is being bubbled up from the bottom or just encountered. - pub last_error_could_be_set: bool, + pub(crate) last_error_could_be_set: bool, /// Indicates that previous executed subtree is complete. /// A subtree treats as a complete if all subtree elements satisfy the following rules: @@ -61,22 +59,19 @@ pub(crate) struct ExecutionCtx<'i> { /// - at least one of xor subtrees is completed without an error /// - all of seq subtrees are completed /// - call executed successfully (executed state is Executed) - pub subtree_complete: bool, - - /// List of met folds used to determine whether a variable can be shadowed. - pub met_folds: VecDeque<&'i str>, + pub(crate) subtree_complete: bool, /// Tracker of all met instructions. - pub tracker: InstructionTracker, + pub(crate) tracker: InstructionTracker, /// Last call request id that was used as an id for call request in outcome. - pub last_call_request_id: u32, + pub(crate) last_call_request_id: u32, /// Contains all executed results from a host side. - pub call_results: CallResults, + pub(crate) call_results: CallResults, /// Tracks all functions that should be called from services. - pub call_requests: CallRequests, + pub(crate) call_requests: CallRequests, } impl<'i> ExecutionCtx<'i> { @@ -117,10 +112,14 @@ use std::fmt::Formatter; impl<'i> Display for ExecutionCtx<'i> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f, "data cache:")?; - for (key, value) in self.scalars.iter() { - writeln!(f, " {} => {}", key, value)?; + writeln!(f, "scalars:")?; + writeln!(f, " {}", self.scalars)?; + + writeln!(f, "streams:")?; + for (name, stream) in self.streams.iter() { + writeln!(f, " {} => {}", name, stream.borrow())?; } + writeln!(f, "current peer id: {}", self.current_peer_id)?; writeln!(f, "subtree complete: {}", self.subtree_complete)?; writeln!(f, "next peer public keys: {:?}", self.next_peer_pks)?; diff --git a/air/src/execution_step/execution_context/mod.rs b/air/src/execution_step/execution_context/mod.rs index 118ac141..483e0e77 100644 --- a/air/src/execution_step/execution_context/mod.rs +++ b/air/src/execution_step/execution_context/mod.rs @@ -16,8 +16,10 @@ mod context; mod error_descriptor; +mod scalar_variables; pub(crate) use context::*; pub use error_descriptor::LastError; pub(crate) use error_descriptor::LastErrorDescriptor; pub(crate) use error_descriptor::LastErrorWithTetraplet; +pub(crate) use scalar_variables::*; diff --git a/air/src/execution_step/execution_context/scalar_variables.rs b/air/src/execution_step/execution_context/scalar_variables.rs new file mode 100644 index 00000000..78b5de2c --- /dev/null +++ b/air/src/execution_step/execution_context/scalar_variables.rs @@ -0,0 +1,229 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::exec_err; +use crate::execution_step::boxed_value::ScalarRef; +use crate::execution_step::ExecutionError; +use crate::execution_step::ExecutionResult; +use crate::execution_step::FoldState; +use crate::execution_step::ValueAggregate; + +use std::collections::HashMap; +use std::rc::Rc; + +/// There are two scopes for variable scalars in AIR: global and local. A local scope +/// is a scope inside every fold block, other scope is a global. It means that scalar +/// in an upper fold block could be shadowed by a scalar with the same name in a lower +/// fold block, it works "as expected". Let's consider the following example: +/// (seq +/// (seq +/// (call ... local) ;; (1) +/// (fold iterable_1 iterator_1 +/// (seq +/// (seq +/// (seq +/// (call ... local) ;; (2) +/// (fold iterable_2 iterator_2 +/// (seq +/// (seq +/// (call ... local) ;; (3) +/// (call ... [local]) ;; local set by (3) will be used +/// ) +/// (next iterator_2) +/// ) +/// ) +/// ) +/// (call ... [local]) ;; local set by (2) will be used +/// ) +/// (next iterator_1) +/// ) +/// ) +/// ) +/// (seq +/// (call ... [local]) ;; local set by (1) will be used +/// (call ... local) ;; error will be occurred because, it's impossible to set variable twice +/// ;; in a global scope +/// ) +/// ) +/// +/// Although there could be only one iterable value for a fold block, because of CRDT rules. +/// This struct is intended to provide abilities to work with scalars as it was described. +#[derive(Default)] +pub(crate) struct Scalars<'i> { + // this one is optimized for speed (not for memory), because it's unexpected + // that a script could have a lot of inner folds. + pub values: HashMap>>, + pub iterable_values: HashMap>, + pub fold_block_id: usize, +} + +#[allow(dead_code)] +impl<'i> Scalars<'i> { + /// Returns true if there was a previous value for the provided key on the same + /// fold block. + 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.values.entry(name.into()) { + Vacant(entry) => { + let mut values = vec![None; self.fold_block_id]; + values.push(Some(value)); + entry.insert(values); + + Ok(false) + } + Occupied(entry) => { + if !shadowing_allowed { + return exec_err!(ExecutionError::MultipleVariablesFound(entry.key().clone())); + } + + let values = entry.into_mut(); + let contains_prev_value = values + .get(self.fold_block_id) + .map_or_else(|| false, |value| value.is_none()); + // could be considered as lazy erasing + values.resize(self.fold_block_id + 1, None); + + values[self.fold_block_id] = Some(value); + Ok(contains_prev_value) + } + } + } + + pub(crate) fn set_iterable_value( + &mut self, + name: impl Into, + fold_state: FoldState<'i>, + ) -> ExecutionResult<()> { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + + match self.iterable_values.entry(name.into()) { + Vacant(entry) => { + entry.insert(fold_state); + Ok(()) + } + Occupied(entry) => { + exec_err!(ExecutionError::MultipleIterableValues(entry.key().clone())) + } + } + } + + pub(crate) fn remove_value(&mut self, name: &str) { + self.values.remove(name); + } + + pub(crate) fn remove_iterable_value(&mut self, name: &str) { + self.iterable_values.remove(name); + } + + pub(crate) fn get_value(&'i self, name: &str) -> ExecutionResult<&'i ValueAggregate> { + self.values + .get(name) + .and_then(|scalars| { + scalars + .iter() + .take(self.fold_block_id + 1) + .rev() + .find_map(|scalar| scalar.as_ref()) + }) + .ok_or_else(|| Rc::new(ExecutionError::VariableNotFound(name.to_string()))) + } + + pub(crate) fn get_value_mut(&'i mut self, name: &str) -> ExecutionResult<&'i mut ValueAggregate> { + let fold_block_id = self.fold_block_id; + self.values + .get_mut(name) + .and_then(|scalars| { + scalars + .iter_mut() + .take(fold_block_id) + .rev() + .find_map(|scalar| scalar.as_mut()) + }) + .ok_or_else(|| Rc::new(ExecutionError::VariableNotFound(name.to_string()))) + } + + pub(crate) fn get_iterable(&self, name: &str) -> ExecutionResult<&FoldState<'i>> { + self.iterable_values + .get(name) + .ok_or_else(|| Rc::new(ExecutionError::FoldStateNotFound(name.to_string()))) + } + + pub(crate) fn get_iterable_mut(&mut self, name: &str) -> ExecutionResult<&mut FoldState<'i>> { + self.iterable_values + .get_mut(name) + .ok_or_else(|| Rc::new(ExecutionError::FoldStateNotFound(name.to_string()))) + } + + pub(crate) fn get(&'i self, name: &str) -> ExecutionResult> { + let value = self.get_value(name); + let iterable_value = self.iterable_values.get(name); + + match (value, iterable_value) { + (Err(_), None) => exec_err!(ExecutionError::VariableNotFound(name.to_string())), + (Ok(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"), + } + } + + pub(crate) fn meet_fold_begin(&mut self) { + self.fold_block_id += 1; + } + + pub(crate) fn meet_fold_end(&mut self) { + self.fold_block_id -= 1; + if self.fold_block_id == 0 { + // lazy cleanup after exiting from a top fold block to the global scope + self.cleanup() + } + } + + 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 + self.fold_block_id != 0 + } + + fn cleanup(&mut self) { + for (_, scalars) in self.values.iter_mut() { + scalars.truncate(self.fold_block_id + 1) + } + } +} + +use std::fmt; + +impl<'i> fmt::Display for Scalars<'i> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "fold_block_id: {}", self.fold_block_id)?; + + for (name, _) in self.values.iter() { + let value = self.get_value(name); + if let Ok(last_value) = value { + writeln!(f, "{} => {}", name, last_value.result)?; + } + } + + for (name, _) in self.iterable_values.iter() { + // it's impossible to print an iterable value for now + writeln!(f, "{} => iterable", name)?; + } + + Ok(()) + } +} diff --git a/air/src/execution_step/mod.rs b/air/src/execution_step/mod.rs index 0f1b653b..51952026 100644 --- a/air/src/execution_step/mod.rs +++ b/air/src/execution_step/mod.rs @@ -24,9 +24,9 @@ mod utils; pub(super) use self::air::ExecutableInstruction; pub(super) use self::air::FoldState; pub(super) use boxed_value::Generation; -pub(super) use boxed_value::ResolvedCallResult; -pub(super) use boxed_value::Scalar; +pub(super) use boxed_value::ScalarRef; pub(super) use boxed_value::Stream; +pub(super) use boxed_value::ValueAggregate; pub(crate) use errors::Catchable; pub(super) use errors::ExecutionError; pub(crate) use errors::Joinable; diff --git a/air/src/execution_step/utils/resolve.rs b/air/src/execution_step/utils/resolve.rs index 394dd9db..6207bbd7 100644 --- a/air/src/execution_step/utils/resolve.rs +++ b/air/src/execution_step/utils/resolve.rs @@ -19,7 +19,6 @@ use crate::execution_step::boxed_value::JValuable; use crate::execution_step::boxed_value::Variable; use crate::execution_step::execution_context::ExecutionCtx; use crate::execution_step::execution_context::LastErrorWithTetraplet; -use crate::execution_step::ExecutionError; use crate::execution_step::ExecutionResult; use crate::JValue; use crate::LambdaAST; @@ -105,7 +104,7 @@ pub(crate) fn resolve_variable<'ctx, 'i>( use crate::execution_step::boxed_value::StreamJvaluableIngredients; match variable { - Variable::Scalar(name) => scalar_to_jvaluable(name, ctx), + Variable::Scalar(name) => Ok(ctx.scalars.get(name)?.into_jvaluable()), Variable::Stream { name, generation } => { match ctx.streams.get(name) { Some(stream) => { @@ -139,18 +138,3 @@ pub(crate) fn apply_lambda<'i>( // it's known that apply_lambda_with_tetraplets returns vec of one value Ok((jvalue[0].clone(), tetraplets)) } - -/// Constructs jvaluable result from scalars by name. -fn scalar_to_jvaluable<'name, 'i, 'ctx>( - name: &'name str, - ctx: &'ctx ExecutionCtx<'i>, -) -> ExecutionResult> { - use ExecutionError::VariableNotFound; - - let value = ctx - .scalars - .get(name) - .ok_or_else(|| VariableNotFound(name.to_string()))?; - - Ok(value.to_jvaluable()) -} diff --git a/air/tests/test_module/instructions/fold.rs b/air/tests/test_module/instructions/fold.rs index b768daad..9694ba98 100644 --- a/air/tests/test_module/instructions/fold.rs +++ b/air/tests/test_module/instructions/fold.rs @@ -157,7 +157,7 @@ fn inner_fold_with_same_iterator() { let result = call_vm!(vm, "", script, "", ""); - assert_eq!(result.ret_code, 1009); + assert_eq!(result.ret_code, 1008); } #[test] diff --git a/air/tests/test_module/instructions/match_.rs b/air/tests/test_module/instructions/match_.rs index 047e3f39..c6209621 100644 --- a/air/tests/test_module/instructions/match_.rs +++ b/air/tests/test_module/instructions/match_.rs @@ -192,11 +192,11 @@ fn match_without_xor() { let result = call_vm!(set_variable_vm, "", &script, "", ""); let result = call_vm!(vm, "", &script, "", result.data); - assert_eq!(result.ret_code, 1012); + assert_eq!(result.ret_code, 1011); let result = call_vm!(vm, "", script, "", result.data); - assert_eq!(result.ret_code, 1012); + assert_eq!(result.ret_code, 1011); } #[test] diff --git a/air/tests/test_module/instructions/mismatch.rs b/air/tests/test_module/instructions/mismatch.rs index 5cdbfd64..303e6855 100644 --- a/air/tests/test_module/instructions/mismatch.rs +++ b/air/tests/test_module/instructions/mismatch.rs @@ -143,11 +143,11 @@ fn mismatch_without_xor() { let result = call_vm!(set_variable_vm, "asd", &script, "", ""); let result = call_vm!(vm, "asd", &script, "", result.data); - assert_eq!(result.ret_code, 1013); + assert_eq!(result.ret_code, 1012); let result = call_vm!(vm, "asd", script, "", result.data); - assert_eq!(result.ret_code, 1013); + assert_eq!(result.ret_code, 1012); } #[test] diff --git a/air/tests/test_module/integration/streams_early_exit.rs b/air/tests/test_module/integration/streams_early_exit.rs index 1f8e264f..c435c8f9 100644 --- a/air/tests/test_module/integration/streams_early_exit.rs +++ b/air/tests/test_module/integration/streams_early_exit.rs @@ -140,7 +140,7 @@ fn par_early_exit() { ]; let setter_3_malicious_data = raw_data_from_trace(setter_3_malicious_trace); let init_result_3 = call_vm!(init, "", &script, init_result_2.data.clone(), setter_3_malicious_data); - assert_eq!(init_result_3.ret_code, 1014); + assert_eq!(init_result_3.ret_code, 1013); let actual_trace = trace_from_result(&init_result_3); let expected_trace = trace_from_result(&init_result_2);