Improve the scalars storing scheme (#162)

This commit is contained in:
Mike Voronov 2021-10-20 23:35:46 +03:00 committed by GitHub
parent 1c55d34981
commit 18f4c0036f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 368 additions and 354 deletions

12
Cargo.lock generated
View File

@ -291,9 +291,9 @@ dependencies = [
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.7.1" version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -935,9 +935,9 @@ dependencies = [
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.11" version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
@ -1074,9 +1074,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.103" version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce"
[[package]] [[package]]
name = "lock_api" name = "lock_api"

View File

@ -17,12 +17,11 @@
mod apply_to_arguments; mod apply_to_arguments;
mod utils; mod utils;
use super::call::call_result_setter::set_scalar_result;
use super::call::call_result_setter::set_stream_result; use super::call::call_result_setter::set_stream_result;
use super::ExecutionCtx; use super::ExecutionCtx;
use super::ExecutionResult; use super::ExecutionResult;
use super::TraceHandler; 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::boxed_value::Variable;
use crate::execution_step::utils::apply_lambda; use crate::execution_step::utils::apply_lambda;
use crate::trace_to_exec_err; use crate::trace_to_exec_err;
@ -69,11 +68,11 @@ impl<'i> super::ExecutableInstruction<'i> for Ap<'i> {
fn save_result<'ctx>( fn save_result<'ctx>(
ap_result_type: &AstVariable<'ctx>, ap_result_type: &AstVariable<'ctx>,
merger_ap_result: &MergerApResult, merger_ap_result: &MergerApResult,
result: ResolvedCallResult, result: ValueAggregate,
exec_ctx: &mut ExecutionCtx<'ctx>, exec_ctx: &mut ExecutionCtx<'ctx>,
) -> ExecutionResult<()> { ) -> ExecutionResult<()> {
match ap_result_type { 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) => { AstVariable::Stream(name) => {
let generation = ap_result_to_generation(merger_ap_result); let generation = ap_result_to_generation(merger_ap_result);
set_stream_result(result, generation, name.to_string(), exec_ctx).map(|_| ()) set_stream_result(result, generation, name.to_string(), exec_ctx).map(|_| ())

View File

@ -21,7 +21,7 @@ pub(super) fn apply_to_arg(
exec_ctx: &ExecutionCtx<'_>, exec_ctx: &ExecutionCtx<'_>,
trace_ctx: &TraceHandler, trace_ctx: &TraceHandler,
should_touch_trace: bool, should_touch_trace: bool,
) -> ExecutionResult<ResolvedCallResult> { ) -> ExecutionResult<ValueAggregate> {
let result = match argument { let result = match argument {
ApArgument::ScalarVariable(scalar_name) => apply_scalar(scalar_name, exec_ctx, trace_ctx, should_touch_trace)?, 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)?, ApArgument::VariableWithLambda(vl) => apply_json_argument(vl, exec_ctx, trace_ctx)?,
@ -40,18 +40,14 @@ fn apply_scalar(
exec_ctx: &ExecutionCtx<'_>, exec_ctx: &ExecutionCtx<'_>,
trace_ctx: &TraceHandler, trace_ctx: &TraceHandler,
should_touch_trace: bool, should_touch_trace: bool,
) -> ExecutionResult<ResolvedCallResult> { ) -> ExecutionResult<ValueAggregate> {
use crate::execution_step::ExecutionError::VariableNotFound; use crate::execution_step::ScalarRef;
use crate::execution_step::Scalar;
let scalar = exec_ctx let scalar = exec_ctx.scalars.get(scalar_name)?;
.scalars
.get(scalar_name)
.ok_or_else(|| VariableNotFound(scalar_name.to_string()))?;
let mut result = match scalar { let mut result = match scalar {
Scalar::JValueRef(result) => result.clone(), ScalarRef::Value(result) => result.clone(),
Scalar::JValueFoldCursor(iterator) => { ScalarRef::IterableValue(iterator) => {
let result = iterator.iterable.peek().expect( let result = iterator.iterable.peek().expect(
"peek always return elements inside fold,\ "peek always return elements inside fold,\
this guaranteed by implementation of next and avoiding empty folds", this guaranteed by implementation of next and avoiding empty folds",
@ -67,24 +63,24 @@ fn apply_scalar(
Ok(result) Ok(result)
} }
fn apply_const(value: impl Into<JValue>, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler) -> ResolvedCallResult { fn apply_const(value: impl Into<JValue>, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler) -> ValueAggregate {
let value = Rc::new(value.into()); let value = Rc::new(value.into());
let tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.init_peer_id.clone()); let tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.init_peer_id.clone());
let tetraplet = Rc::new(RefCell::new(tetraplet)); 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( fn apply_last_error(
error_path: &LastErrorPath, error_path: &LastErrorPath,
exec_ctx: &ExecutionCtx<'_>, exec_ctx: &ExecutionCtx<'_>,
trace_ctx: &TraceHandler, trace_ctx: &TraceHandler,
) -> ExecutionResult<ResolvedCallResult> { ) -> ExecutionResult<ValueAggregate> {
let (value, mut tetraplets) = crate::execution_step::utils::prepare_last_error(error_path, exec_ctx)?; let (value, mut tetraplets) = crate::execution_step::utils::prepare_last_error(error_path, exec_ctx)?;
let value = Rc::new(value); let value = Rc::new(value);
let tetraplet = tetraplets.remove(0); 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) Ok(result)
} }
@ -92,14 +88,14 @@ fn apply_json_argument(
vl: &VariableWithLambda<'_>, vl: &VariableWithLambda<'_>,
exec_ctx: &ExecutionCtx<'_>, exec_ctx: &ExecutionCtx<'_>,
trace_ctx: &TraceHandler, trace_ctx: &TraceHandler,
) -> ExecutionResult<ResolvedCallResult> { ) -> ExecutionResult<ValueAggregate> {
let variable = Variable::from_ast(&vl.variable); let variable = Variable::from_ast(&vl.variable);
let (jvalue, mut tetraplets) = apply_lambda(variable, &vl.lambda, exec_ctx)?; let (jvalue, mut tetraplets) = apply_lambda(variable, &vl.lambda, exec_ctx)?;
let tetraplet = tetraplets let tetraplet = tetraplets
.pop() .pop()
.unwrap_or_else(|| Rc::new(RefCell::new(SecurityTetraplet::default()))); .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) Ok(result)
} }

View File

@ -15,13 +15,11 @@
*/ */
use super::*; use super::*;
use crate::exec_err;
use crate::execution_step::execution_context::*; use crate::execution_step::execution_context::*;
use crate::execution_step::AstVariable; use crate::execution_step::AstVariable;
use crate::execution_step::Generation; use crate::execution_step::Generation;
use crate::execution_step::ResolvedCallResult;
use crate::execution_step::Scalar;
use crate::execution_step::Stream; use crate::execution_step::Stream;
use crate::execution_step::ValueAggregate;
use air_interpreter_data::CallResult; use air_interpreter_data::CallResult;
use air_interpreter_data::Value; 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`. /// Writes result of a local `Call` instruction to `ExecutionCtx` at `output`.
/// Returns call result. /// Returns call result.
pub(crate) fn set_local_result<'i>( pub(crate) fn set_local_result<'i>(
executed_result: ResolvedCallResult, executed_result: ValueAggregate,
output: &CallOutputValue<'i>, output: &CallOutputValue<'i>,
exec_ctx: &mut ExecutionCtx<'i>, exec_ctx: &mut ExecutionCtx<'i>,
) -> ExecutionResult<CallResult> { ) -> ExecutionResult<CallResult> {
let result_value = executed_result.result.clone(); let result_value = executed_result.result.clone();
match output { match output {
CallOutputValue::Variable(AstVariable::Scalar(name)) => { 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)) Ok(CallResult::executed_scalar(result_value))
} }
CallOutputValue::Variable(AstVariable::Stream(name)) => { CallOutputValue::Variable(AstVariable::Stream(name)) => {
@ -67,11 +65,11 @@ pub(crate) fn set_result_from_value<'i>(
) -> ExecutionResult<()> { ) -> ExecutionResult<()> {
match (output, value) { match (output, value) {
(CallOutputValue::Variable(AstVariable::Scalar(name)), Value::Scalar(value)) => { (CallOutputValue::Variable(AstVariable::Scalar(name)), Value::Scalar(value)) => {
let result = ResolvedCallResult::new(value, tetraplet, trace_pos); let result = ValueAggregate::new(value, tetraplet, trace_pos);
set_scalar_result(result, name, exec_ctx)?; exec_ctx.scalars.set_value(*name, result)?;
} }
(CallOutputValue::Variable(AstVariable::Stream(name)), Value::Stream { value, generation }) => { (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 generation = Generation::Nth(generation);
let _ = set_stream_result(result, generation, name.to_string(), exec_ctx)?; let _ = set_stream_result(result, generation, name.to_string(), exec_ctx)?;
} }
@ -83,68 +81,9 @@ pub(crate) fn set_result_from_value<'i>(
Ok(()) 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 // TODO: decouple this function to a separate module
pub(crate) fn set_stream_result( pub(crate) fn set_stream_result(
executed_result: ResolvedCallResult, executed_result: ValueAggregate,
generation: Generation, generation: Generation,
stream_name: String, stream_name: String,
exec_ctx: &mut ExecutionCtx<'_>, exec_ctx: &mut ExecutionCtx<'_>,

View File

@ -85,7 +85,7 @@ pub(super) fn handle_prev_state<'i>(
} }
use super::call_result_setter::*; use super::call_result_setter::*;
use crate::execution_step::ResolvedCallResult; use crate::execution_step::ValueAggregate;
use crate::JValue; use crate::JValue;
fn update_state_with_service_result<'i>( 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 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)?; let new_call_result = set_local_result(executed_result, output, exec_ctx)?;
trace_ctx.meet_call_end(new_call_result); trace_ctx.meet_call_end(new_call_result);

View File

@ -169,7 +169,7 @@ impl<'i> ResolvedCall<'i> {
/// Check output type name for being already in execution context. /// Check output type name for being already in execution context.
// TODO: this check should be moved on a parsing stage // TODO: this check should be moved on a parsing stage
fn check_output_name(output: &CallOutputValue<'_>, exec_ctx: &ExecutionCtx<'_>) -> ExecutionResult<()> { 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 { let scalar_name = match output {
CallOutputValue::Variable(AstVariable::Scalar(name)) => *name, 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) { match exec_ctx.scalars.get(scalar_name) {
Some(Scalar::JValueRef(_)) => { Ok(ScalarRef::Value(_)) => {
if exec_ctx.met_folds.is_empty() { if exec_ctx.scalars.shadowing_allowed() {
// shadowing is allowed only inside fold blocks
crate::exec_err!(ExecutionError::MultipleVariablesFound(scalar_name.to_string()))
} else {
Ok(()) Ok(())
} else {
crate::exec_err!(ExecutionError::MultipleVariablesFound(scalar_name.to_string()))
} }
} }
Some(_) => crate::exec_err!(ExecutionError::IterableShadowing(scalar_name.to_string())), Ok(ScalarRef::IterableValue(_)) => crate::exec_err!(ExecutionError::IterableShadowing(scalar_name.to_string())),
None => Ok(()), Err(_) => Ok(()),
} }
} }

View File

@ -16,17 +16,13 @@
use super::Instruction; use super::Instruction;
use super::IterableValue; use super::IterableValue;
use super::ResolvedCallResult;
use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
pub(crate) struct FoldState<'i> { pub(crate) struct FoldState<'i> {
pub(crate) iterable: IterableValue, pub(crate) iterable: IterableValue,
pub(crate) iterable_type: IterableType, pub(crate) iterable_type: IterableType,
pub(crate) instr_head: Rc<Instruction<'i>>, pub(crate) instr_head: Rc<Instruction<'i>>,
// 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)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -45,7 +41,6 @@ impl<'i> FoldState<'i> {
iterable, iterable,
iterable_type, iterable_type,
instr_head, instr_head,
met_variables: HashMap::new(),
} }
} }
} }

View File

@ -16,17 +16,15 @@
mod fold_state; mod fold_state;
mod utils; mod utils;
mod variable_handler;
pub(crate) use fold_state::FoldState; pub(crate) use fold_state::FoldState;
pub(crate) use fold_state::IterableType; pub(crate) use fold_state::IterableType;
pub(super) use utils::*; pub(super) use utils::*;
pub(super) use variable_handler::VariableHandler;
use super::ExecutionCtx; use super::ExecutionCtx;
use super::ExecutionError; use super::ExecutionError;
use super::ExecutionResult; use super::ExecutionResult;
use super::Instruction; use super::Instruction;
use super::ResolvedCallResult; use super::ScalarRef;
use super::Scalar; use super::ValueAggregate;
use crate::execution_step::boxed_value::*; use crate::execution_step::boxed_value::*;

View File

@ -88,19 +88,18 @@ fn create_scalar_iterable<'ctx>(
exec_ctx: &ExecutionCtx<'ctx>, exec_ctx: &ExecutionCtx<'ctx>,
variable_name: &str, variable_name: &str,
) -> ExecutionResult<FoldIterableScalar> { ) -> ExecutionResult<FoldIterableScalar> {
match exec_ctx.scalars.get(variable_name) { match exec_ctx.scalars.get(variable_name)? {
Some(Scalar::JValueRef(call_result)) => from_call_result(call_result.clone()), ScalarRef::Value(call_result) => from_call_result(call_result.clone()),
Some(Scalar::JValueFoldCursor(fold_state)) => { ScalarRef::IterableValue(fold_state) => {
let iterable_value = fold_state.iterable.peek().unwrap(); let iterable_value = fold_state.iterable.peek().unwrap();
let call_result = iterable_value.into_resolved_result(); let call_result = iterable_value.into_resolved_result();
from_call_result(call_result) from_call_result(call_result)
} }
_ => return exec_err!(ExecutionError::VariableNotFound(variable_name.to_string())),
} }
} }
/// Constructs iterable value from resolved call result. /// Constructs iterable value from resolved call result.
fn from_call_result(call_result: ResolvedCallResult) -> ExecutionResult<FoldIterableScalar> { fn from_call_result(call_result: ValueAggregate) -> ExecutionResult<FoldIterableScalar> {
use ExecutionError::IncompatibleJValueType; use ExecutionError::IncompatibleJValueType;
let len = match &call_result.result.deref() { let len = match &call_result.result.deref() {
@ -128,19 +127,18 @@ fn create_scalar_lambda_iterable<'ctx>(
) -> ExecutionResult<FoldIterableScalar> { ) -> ExecutionResult<FoldIterableScalar> {
use crate::execution_step::lambda_applier::select; use crate::execution_step::lambda_applier::select;
match exec_ctx.scalars.get(scalar_name) { match exec_ctx.scalars.get(scalar_name)? {
Some(Scalar::JValueRef(variable)) => { ScalarRef::Value(variable) => {
let jvalues = select(&variable.result, lambda.iter())?; let jvalues = select(&variable.result, lambda.iter())?;
from_jvalue(jvalues, variable.tetraplet.clone(), lambda) 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 iterable_value = fold_state.iterable.peek().unwrap();
let jvalues = iterable_value.apply_lambda(lambda)?; let jvalues = iterable_value.apply_lambda(lambda)?;
let tetraplet = as_tetraplet(&iterable_value); let tetraplet = as_tetraplet(&iterable_value);
from_jvalue(jvalues[0], tetraplet, lambda) from_jvalue(jvalues[0], tetraplet, lambda)
} }
_ => return exec_err!(ExecutionError::VariableNotFound(scalar_name.to_string())),
} }
} }

View File

@ -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> {
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);
}
}

View File

@ -29,7 +29,9 @@ impl<'i> ExecutableInstruction<'i> for FoldScalar<'i> {
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
log_instruction!(fold, exec_ctx, trace_ctx); 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::Empty => Ok(()),
FoldIterableScalar::Scalar(iterable) => fold( FoldIterableScalar::Scalar(iterable) => fold(
iterable, iterable,
@ -39,7 +41,11 @@ impl<'i> ExecutableInstruction<'i> for FoldScalar<'i> {
exec_ctx, exec_ctx,
trace_ctx, trace_ctx,
), ),
} };
exec_ctx.scalars.meet_fold_end();
fold_result
} }
} }
@ -52,11 +58,11 @@ pub(super) fn fold<'i>(
trace_ctx: &mut TraceHandler, trace_ctx: &mut TraceHandler,
) -> ExecutionResult<()> { ) -> ExecutionResult<()> {
let fold_state = FoldState::from_iterable(iterable, iterable_type, instruction.clone()); 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)?; instruction.execute(exec_ctx, trace_ctx)?;
variable_handler.cleanup(exec_ctx); exec_ctx.scalars.remove_iterable_value(iterator);
Ok(()) Ok(())
} }

View File

@ -36,6 +36,7 @@ impl<'i> ExecutableInstruction<'i> for FoldStream<'i> {
let fold_id = exec_ctx.tracker.fold.seen_stream_count; let fold_id = exec_ctx.tracker.fold.seen_stream_count;
trace_to_exec_err!(trace_ctx.meet_fold_start(fold_id))?; trace_to_exec_err!(trace_ctx.meet_fold_start(fold_id))?;
exec_ctx.scalars.meet_fold_begin();
for iterable in iterables { for iterable in iterables {
let value = match iterable.peek() { 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))?; trace_to_exec_err!(trace_ctx.meet_fold_end(fold_id))?;
exec_ctx.scalars.meet_fold_end();
Ok(()) Ok(())
} }

View File

@ -30,8 +30,8 @@ mod xor;
pub(crate) use fold::FoldState; pub(crate) use fold::FoldState;
use super::boxed_value::ResolvedCallResult; use super::boxed_value::ScalarRef;
use super::boxed_value::Scalar; use super::boxed_value::ValueAggregate;
use super::execution_context::*; use super::execution_context::*;
use super::Catchable; use super::Catchable;
use super::ExecutionCtx; use super::ExecutionCtx;
@ -147,12 +147,8 @@ macro_rules! log_instruction {
log::debug!(target: air_log_targets::INSTRUCTION, "> {}", stringify!($instr_name)); log::debug!(target: air_log_targets::INSTRUCTION, "> {}", stringify!($instr_name));
let mut variables = String::from(" scalars:"); let mut variables = String::from(" scalars:");
if $exec_ctx.scalars.is_empty() {
variables.push_str(" empty"); variables.push_str(&format!("\n {}", $exec_ctx.scalars));
}
for (key, value) in $exec_ctx.scalars.iter() {
variables.push_str(&format!("\n {} => {}", key, value));
}
variables.push_str(" streams:"); variables.push_str(" streams:");
if $exec_ctx.streams.is_empty() { if $exec_ctx.streams.is_empty() {

View File

@ -16,12 +16,9 @@
use super::fold::IterableType; use super::fold::IterableType;
use super::ExecutionCtx; use super::ExecutionCtx;
use super::ExecutionError;
use super::ExecutionResult; use super::ExecutionResult;
use super::FoldState; use super::FoldState;
use super::Scalar;
use super::TraceHandler; use super::TraceHandler;
use crate::exec_err;
use crate::log_instruction; use crate::log_instruction;
use crate::trace_to_exec_err; 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); log_instruction!(next, exec_ctx, trace_ctx);
let iterator_name = self.0; 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)?; maybe_meet_iteration_end(fold_state, trace_ctx)?;
if !fold_state.iterable.next() { if !fold_state.iterable.next() {
@ -48,46 +45,14 @@ impl<'i> super::ExecutableInstruction<'i> for Next<'i> {
next_instr.execute(exec_ctx, trace_ctx)?; next_instr.execute(exec_ctx, trace_ctx)?;
// get the same fold state again because of borrow checker // get the same fold state again because of borrow checker
match exec_ctx.scalars.get_mut(iterator_name) { let fold_state = exec_ctx.scalars.get_iterable_mut(iterator_name)?;
// move iterator back to provide correct value for possible subtree after next fold_state.iterable.prev();
// (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)?;
maybe_meet_back_iterator(fold_state, trace_ctx)?; maybe_meet_back_iterator(fold_state, trace_ctx)?;
Ok(()) 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<()> { fn maybe_meet_iteration_start(fold_state: &FoldState<'_>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
if let IterableType::Stream(fold_id) = &fold_state.iterable_type { 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()))?; trace_to_exec_err!(trace_ctx.meet_iteration_start(*fold_id, fold_state.iterable.peek().unwrap().pos()))?;

View File

@ -23,7 +23,7 @@ pub(crate) use json_path_result::IterableLambdaResult;
pub(crate) use resolved_call::IterableResolvedCall; pub(crate) use resolved_call::IterableResolvedCall;
pub(crate) use vec_resolved_call::IterableVecResolvedCall; pub(crate) use vec_resolved_call::IterableVecResolvedCall;
use super::ResolvedCallResult; use super::ValueAggregate;
use crate::execution_step::RSecurityTetraplet; use crate::execution_step::RSecurityTetraplet;
use crate::JValue; use crate::JValue;
@ -74,7 +74,7 @@ impl IterableItem<'_> {
*pos *pos
} }
pub(crate) fn into_resolved_result(self) -> ResolvedCallResult { pub(crate) fn into_resolved_result(self) -> ValueAggregate {
use IterableItem::*; use IterableItem::*;
let (value, tetraplet, pos) = match self { let (value, tetraplet, pos) = match self {
@ -83,7 +83,7 @@ impl IterableItem<'_> {
RcValue(ingredients) => ingredients, RcValue(ingredients) => ingredients,
}; };
ResolvedCallResult::new(value, tetraplet, pos) ValueAggregate::new(value, tetraplet, pos)
} }
} }

View File

@ -16,7 +16,7 @@
use super::Iterable; use super::Iterable;
use super::IterableItem; use super::IterableItem;
use super::ResolvedCallResult; use super::ValueAggregate;
use crate::foldable_next; use crate::foldable_next;
use crate::foldable_prev; use crate::foldable_prev;
use crate::JValue; use crate::JValue;
@ -26,13 +26,13 @@ use std::ops::Deref;
/// Used for iterating over JValue of array type. /// Used for iterating over JValue of array type.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct IterableResolvedCall { pub(crate) struct IterableResolvedCall {
pub(crate) call_result: ResolvedCallResult, pub(crate) call_result: ValueAggregate,
pub(crate) cursor: usize, pub(crate) cursor: usize,
pub(crate) len: usize, pub(crate) len: usize,
} }
impl IterableResolvedCall { impl IterableResolvedCall {
pub(crate) fn init(call_result: ResolvedCallResult, len: usize) -> Self { pub(crate) fn init(call_result: ValueAggregate, len: usize) -> Self {
Self { Self {
call_result, call_result,
cursor: 0, cursor: 0,
@ -57,7 +57,7 @@ impl<'ctx> Iterable<'ctx> for IterableResolvedCall {
return None; return None;
} }
let ResolvedCallResult { let ValueAggregate {
result, result,
tetraplet, tetraplet,
trace_pos, trace_pos,

View File

@ -16,19 +16,19 @@
use super::Iterable; use super::Iterable;
use super::IterableItem; use super::IterableItem;
use super::ResolvedCallResult; use super::ValueAggregate;
use crate::foldable_next; use crate::foldable_next;
use crate::foldable_prev; use crate::foldable_prev;
/// Used for iterating over stream with JValues. /// Used for iterating over stream with JValues.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct IterableVecResolvedCall { pub(crate) struct IterableVecResolvedCall {
pub(crate) call_results: Vec<ResolvedCallResult>, pub(crate) call_results: Vec<ValueAggregate>,
pub(crate) cursor: usize, pub(crate) cursor: usize,
} }
impl IterableVecResolvedCall { impl IterableVecResolvedCall {
pub(crate) fn init(call_results: Vec<ResolvedCallResult>) -> Self { pub(crate) fn init(call_results: Vec<ValueAggregate>) -> Self {
Self { Self {
call_results, call_results,
cursor: 0, cursor: 0,
@ -52,7 +52,7 @@ impl<'ctx> Iterable<'ctx> for IterableVecResolvedCall {
return None; return None;
} }
let ResolvedCallResult { let ValueAggregate {
result, result,
tetraplet, tetraplet,
trace_pos, trace_pos,

View File

@ -23,7 +23,7 @@ mod stream;
use super::iterable::IterableItem; use super::iterable::IterableItem;
use super::ExecutionError; use super::ExecutionError;
use super::ExecutionResult; use super::ExecutionResult;
use super::ResolvedCallResult; use super::ValueAggregate;
use crate::execution_step::lambda_applier::*; use crate::execution_step::lambda_applier::*;
use crate::execution_step::SecurityTetraplets; use crate::execution_step::SecurityTetraplets;
use crate::JValue; use crate::JValue;

View File

@ -17,7 +17,7 @@
use super::select_from_stream; use super::select_from_stream;
use super::ExecutionResult; use super::ExecutionResult;
use super::JValuable; use super::JValuable;
use super::ResolvedCallResult; use super::ValueAggregate;
use crate::execution_step::SecurityTetraplets; use crate::execution_step::SecurityTetraplets;
use crate::JValue; use crate::JValue;
use crate::LambdaAST; use crate::LambdaAST;
@ -27,7 +27,7 @@ use air_lambda_ast::format_ast;
use std::borrow::Cow; use std::borrow::Cow;
use std::ops::Deref; use std::ops::Deref;
impl JValuable for std::cell::Ref<'_, Vec<ResolvedCallResult>> { impl JValuable for std::cell::Ref<'_, Vec<ValueAggregate>> {
fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<Vec<&JValue>> { fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<Vec<&JValue>> {
let stream_iter = self.iter().map(|r| r.result.deref()); let stream_iter = self.iter().map(|r| r.result.deref());
let select_result = select_from_stream(stream_iter, lambda)?; let select_result = select_from_stream(stream_iter, lambda)?;

View File

@ -18,7 +18,7 @@ use super::select;
use super::ExecutionResult; use super::ExecutionResult;
use super::JValuable; use super::JValuable;
use super::LambdaAST; use super::LambdaAST;
use super::ResolvedCallResult; use super::ValueAggregate;
use crate::execution_step::SecurityTetraplets; use crate::execution_step::SecurityTetraplets;
use crate::JValue; use crate::JValue;
@ -27,7 +27,7 @@ use air_lambda_ast::format_ast;
use std::borrow::Cow; use std::borrow::Cow;
use std::ops::Deref; use std::ops::Deref;
impl JValuable for ResolvedCallResult { impl JValuable for ValueAggregate {
fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<Vec<&JValue>> { fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<Vec<&JValue>> {
let selected_value = select(&self.result, lambda.iter())?; let selected_value = select(&self.result, lambda.iter())?;
Ok(vec![selected_value]) Ok(vec![selected_value])

View File

@ -23,8 +23,8 @@ mod variable;
pub(crate) use super::ExecutionError; pub(crate) use super::ExecutionError;
pub(crate) use iterable::*; pub(crate) use iterable::*;
pub(crate) use jvaluable::*; pub(crate) use jvaluable::*;
pub(crate) use scalar::ResolvedCallResult; pub(crate) use scalar::ScalarRef;
pub(crate) use scalar::Scalar; pub(crate) use scalar::ValueAggregate;
pub(crate) use stream::Generation; pub(crate) use stream::Generation;
pub(crate) use stream::Stream; pub(crate) use stream::Stream;
pub(crate) use stream::StreamIter; pub(crate) use stream::StreamIter;

View File

@ -27,22 +27,22 @@ use std::fmt::Formatter;
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct ResolvedCallResult { pub struct ValueAggregate {
pub result: Rc<JValue>, pub result: Rc<JValue>,
pub tetraplet: RSecurityTetraplet, pub tetraplet: RSecurityTetraplet,
pub trace_pos: usize, pub trace_pos: usize,
} }
pub(crate) enum Scalar<'i> { pub(crate) enum ScalarRef<'i> {
JValueRef(ResolvedCallResult), Value(&'i ValueAggregate),
JValueFoldCursor(FoldState<'i>), IterableValue(&'i FoldState<'i>),
} }
impl<'i> Scalar<'i> { impl<'i> ScalarRef<'i> {
pub(crate) fn to_jvaluable<'ctx>(&'ctx self) -> Box<dyn JValuable + 'ctx> { pub(crate) fn into_jvaluable(self) -> Box<dyn JValuable + 'i> {
match self { match self {
Scalar::JValueRef(value) => Box::new(value.clone()), ScalarRef::Value(value) => Box::new(value.clone()),
Scalar::JValueFoldCursor(fold_state) => { ScalarRef::IterableValue(fold_state) => {
let peeked_value = fold_state.iterable.peek().unwrap(); let peeked_value = fold_state.iterable.peek().unwrap();
Box::new(peeked_value) Box::new(peeked_value)
} }
@ -50,7 +50,7 @@ impl<'i> Scalar<'i> {
} }
} }
impl ResolvedCallResult { impl ValueAggregate {
pub(crate) fn new(result: Rc<JValue>, tetraplet: RSecurityTetraplet, trace_pos: usize) -> Self { pub(crate) fn new(result: Rc<JValue>, tetraplet: RSecurityTetraplet, trace_pos: usize) -> Self {
Self { Self {
result, 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 { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
Scalar::JValueRef(value) => write!(f, "{:?}", value)?, ScalarRef::Value(value) => write!(f, "{:?}", value)?,
Scalar::JValueFoldCursor(cursor) => { ScalarRef::IterableValue(cursor) => {
let iterable = &cursor.iterable; let iterable = &cursor.iterable;
write!(f, "cursor, current value: {:?}", iterable.peek())?; write!(f, "cursor, current value: {:?}", iterable.peek())?;
} }

View File

@ -16,7 +16,7 @@
use super::ExecutionError; use super::ExecutionError;
use super::ExecutionResult; use super::ExecutionResult;
use super::ResolvedCallResult; use super::ValueAggregate;
use crate::exec_err; use crate::exec_err;
use crate::JValue; 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. /// 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. // TODO: make it non-pub after boxed value refactoring.
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub(crate) struct Stream(Vec<Vec<ResolvedCallResult>>); pub(crate) struct Stream(Vec<Vec<ValueAggregate>>);
impl Stream { impl Stream {
pub(crate) fn from_generations_count(count: usize) -> Self { pub(crate) fn from_generations_count(count: usize) -> Self {
Self(vec![vec![]; count + 1]) 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]]) Self(vec![vec![value]])
} }
// if generation is None, value would be added to the last generation, otherwise it would // if generation is None, value would be added to the last generation, otherwise it would
// be added to given generation // be added to given generation
pub(crate) fn add_value(&mut self, value: ResolvedCallResult, generation: Generation) -> ExecutionResult<u32> { pub(crate) fn add_value(&mut self, value: ValueAggregate, generation: Generation) -> ExecutionResult<u32> {
let generation = match generation { let generation = match generation {
Generation::Last => self.0.len() - 1, Generation::Last => self.0.len() - 1,
Generation::Nth(id) => id as usize, Generation::Nth(id) => id as usize,
@ -95,7 +95,7 @@ impl Stream {
} }
pub(crate) fn iter(&self, generation: Generation) -> Option<StreamIter<'_>> { pub(crate) fn iter(&self, generation: Generation) -> Option<StreamIter<'_>> {
let iter: Box<dyn Iterator<Item = &ResolvedCallResult>> = match generation { let iter: Box<dyn Iterator<Item = &ValueAggregate>> = match generation {
Generation::Nth(generation) if generation as usize >= self.generations_count() => return None, 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::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())), 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<StreamSliceIter<'_>> { pub(crate) fn slice_iter(&self, generation: Generation) -> Option<StreamSliceIter<'_>> {
let iter: Box<dyn Iterator<Item = &[ResolvedCallResult]>> = match generation { let iter: Box<dyn Iterator<Item = &[ValueAggregate]>> = match generation {
Generation::Nth(generation) if generation as usize >= self.generations_count() => return None, 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::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())), Generation::Last => Box::new(self.0.iter().map(|v| v.as_slice())),
@ -142,12 +142,12 @@ impl Generation {
} }
pub(crate) struct StreamIter<'result> { pub(crate) struct StreamIter<'result> {
iter: Box<dyn Iterator<Item = &'result ResolvedCallResult> + 'result>, iter: Box<dyn Iterator<Item = &'result ValueAggregate> + 'result>,
len: usize, len: usize,
} }
impl<'result> Iterator for StreamIter<'result> { impl<'result> Iterator for StreamIter<'result> {
type Item = &'result ResolvedCallResult; type Item = &'result ValueAggregate;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.len > 0 { if self.len > 0 {
@ -164,12 +164,12 @@ impl<'result> Iterator for StreamIter<'result> {
impl<'result> ExactSizeIterator for StreamIter<'result> {} impl<'result> ExactSizeIterator for StreamIter<'result> {}
pub(crate) struct StreamSliceIter<'slice> { pub(crate) struct StreamSliceIter<'slice> {
iter: Box<dyn Iterator<Item = &'slice [ResolvedCallResult]> + 'slice>, iter: Box<dyn Iterator<Item = &'slice [ValueAggregate]> + 'slice>,
len: usize, len: usize,
} }
impl<'slice> Iterator for StreamSliceIter<'slice> { impl<'slice> Iterator for StreamSliceIter<'slice> {
type Item = &'slice [ResolvedCallResult]; type Item = &'slice [ValueAggregate];
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.len > 0 { if self.len > 0 {

View File

@ -62,21 +62,17 @@ pub(crate) enum ExecutionError {
#[error("lambda is applied to an empty stream")] #[error("lambda is applied to an empty stream")]
EmptyStreamLambdaError, 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")] #[error("expected JValue type '{1}', but got '{0}' JValue")]
IncompatibleJValueType(JValue, &'static str), 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. /// Fold state wasn't found for such iterator name.
#[error("fold state not found for this iterable '{0}'")] #[error("fold state not found for this iterable '{0}'")]
FoldStateNotFound(String), FoldStateNotFound(String),
/// Multiple fold states found for such iterator name. /// Multiple fold states found for such iterator name.
#[error("multiple fold states found for iterable '{0}'")] #[error("multiple iterable values found for iterable name '{0}'")]
MultipleFoldStates(String), MultipleIterableValues(String),
/// A fold instruction must iterate over array value. /// A fold instruction must iterate over array value.
#[error("lambda '{1}' returned non-array value '{0}' for fold instruction")] #[error("lambda '{1}' returned non-array value '{0}' for fold instruction")]

View File

@ -16,7 +16,7 @@
use super::LastErrorDescriptor; use super::LastErrorDescriptor;
use super::LastErrorWithTetraplet; use super::LastErrorWithTetraplet;
use crate::execution_step::boxed_value::Scalar; use super::Scalars;
use crate::execution_step::boxed_value::Stream; use crate::execution_step::boxed_value::Stream;
use air_execution_info_collector::InstructionTracker; use air_execution_info_collector::InstructionTracker;
@ -24,36 +24,34 @@ use air_interpreter_interface::*;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::VecDeque;
use std::rc::Rc; use std::rc::Rc;
/// Contains all necessary state needed to execute AIR script. /// Contains all necessary state needed to execute AIR script.
#[derive(Default)] #[derive(Default)]
pub(crate) struct ExecutionCtx<'i> { pub(crate) struct ExecutionCtx<'i> {
/// Contains all scalars. /// Contains all scalars.
// TODO: use shared string (Rc<String>) to avoid copying. pub(crate) scalars: Scalars<'i>,
pub scalars: HashMap<String, Scalar<'i>>,
/// Contains all streams. /// Contains all streams.
// TODO: use shared string (Rc<String>) to avoid copying. // TODO: use shared string (Rc<String>) to avoid copying.
pub streams: HashMap<String, RefCell<Stream>>, pub(crate) streams: HashMap<String, RefCell<Stream>>,
/// Set of peer public keys that should receive resulted data. /// Set of peer public keys that should receive resulted data.
pub next_peer_pks: Vec<String>, pub(crate) next_peer_pks: Vec<String>,
/// PeerId of a peer executing this AIR script at the moment. /// PeerId of a peer executing this AIR script at the moment.
pub current_peer_id: Rc<String>, pub(crate) current_peer_id: Rc<String>,
/// PeerId of a peer send this AIR script. /// 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. /// Last error produced by local service.
/// None means that there weren't any error. /// None means that there weren't any error.
pub last_error: Option<LastErrorDescriptor>, pub(crate) last_error: Option<LastErrorDescriptor>,
/// True, if last error could be set. This flag is used to distinguish /// 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. /// 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. /// Indicates that previous executed subtree is complete.
/// A subtree treats as a complete if all subtree elements satisfy the following rules: /// 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 /// - at least one of xor subtrees is completed without an error
/// - all of seq subtrees are completed /// - all of seq subtrees are completed
/// - call executed successfully (executed state is Executed) /// - call executed successfully (executed state is Executed)
pub subtree_complete: bool, pub(crate) subtree_complete: bool,
/// List of met folds used to determine whether a variable can be shadowed.
pub met_folds: VecDeque<&'i str>,
/// Tracker of all met instructions. /// 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. /// 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. /// 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. /// Tracks all functions that should be called from services.
pub call_requests: CallRequests, pub(crate) call_requests: CallRequests,
} }
impl<'i> ExecutionCtx<'i> { impl<'i> ExecutionCtx<'i> {
@ -117,10 +112,14 @@ use std::fmt::Formatter;
impl<'i> Display for ExecutionCtx<'i> { impl<'i> Display for ExecutionCtx<'i> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "data cache:")?; writeln!(f, "scalars:")?;
for (key, value) in self.scalars.iter() { writeln!(f, " {}", self.scalars)?;
writeln!(f, " {} => {}", key, value)?;
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, "current peer id: {}", self.current_peer_id)?;
writeln!(f, "subtree complete: {}", self.subtree_complete)?; writeln!(f, "subtree complete: {}", self.subtree_complete)?;
writeln!(f, "next peer public keys: {:?}", self.next_peer_pks)?; writeln!(f, "next peer public keys: {:?}", self.next_peer_pks)?;

View File

@ -16,8 +16,10 @@
mod context; mod context;
mod error_descriptor; mod error_descriptor;
mod scalar_variables;
pub(crate) use context::*; pub(crate) use context::*;
pub use error_descriptor::LastError; pub use error_descriptor::LastError;
pub(crate) use error_descriptor::LastErrorDescriptor; pub(crate) use error_descriptor::LastErrorDescriptor;
pub(crate) use error_descriptor::LastErrorWithTetraplet; pub(crate) use error_descriptor::LastErrorWithTetraplet;
pub(crate) use scalar_variables::*;

View File

@ -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<String, Vec<Option<ValueAggregate>>>,
pub iterable_values: HashMap<String, FoldState<'i>>,
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<String>, value: ValueAggregate) -> ExecutionResult<bool> {
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<String>,
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<ScalarRef<'i>> {
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(())
}
}

View File

@ -24,9 +24,9 @@ mod utils;
pub(super) use self::air::ExecutableInstruction; pub(super) use self::air::ExecutableInstruction;
pub(super) use self::air::FoldState; pub(super) use self::air::FoldState;
pub(super) use boxed_value::Generation; pub(super) use boxed_value::Generation;
pub(super) use boxed_value::ResolvedCallResult; pub(super) use boxed_value::ScalarRef;
pub(super) use boxed_value::Scalar;
pub(super) use boxed_value::Stream; pub(super) use boxed_value::Stream;
pub(super) use boxed_value::ValueAggregate;
pub(crate) use errors::Catchable; pub(crate) use errors::Catchable;
pub(super) use errors::ExecutionError; pub(super) use errors::ExecutionError;
pub(crate) use errors::Joinable; pub(crate) use errors::Joinable;

View File

@ -19,7 +19,6 @@ use crate::execution_step::boxed_value::JValuable;
use crate::execution_step::boxed_value::Variable; use crate::execution_step::boxed_value::Variable;
use crate::execution_step::execution_context::ExecutionCtx; use crate::execution_step::execution_context::ExecutionCtx;
use crate::execution_step::execution_context::LastErrorWithTetraplet; use crate::execution_step::execution_context::LastErrorWithTetraplet;
use crate::execution_step::ExecutionError;
use crate::execution_step::ExecutionResult; use crate::execution_step::ExecutionResult;
use crate::JValue; use crate::JValue;
use crate::LambdaAST; use crate::LambdaAST;
@ -105,7 +104,7 @@ pub(crate) fn resolve_variable<'ctx, 'i>(
use crate::execution_step::boxed_value::StreamJvaluableIngredients; use crate::execution_step::boxed_value::StreamJvaluableIngredients;
match variable { match variable {
Variable::Scalar(name) => scalar_to_jvaluable(name, ctx), Variable::Scalar(name) => Ok(ctx.scalars.get(name)?.into_jvaluable()),
Variable::Stream { name, generation } => { Variable::Stream { name, generation } => {
match ctx.streams.get(name) { match ctx.streams.get(name) {
Some(stream) => { 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 // it's known that apply_lambda_with_tetraplets returns vec of one value
Ok((jvalue[0].clone(), tetraplets)) 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<Box<dyn JValuable + 'ctx>> {
use ExecutionError::VariableNotFound;
let value = ctx
.scalars
.get(name)
.ok_or_else(|| VariableNotFound(name.to_string()))?;
Ok(value.to_jvaluable())
}

View File

@ -157,7 +157,7 @@ fn inner_fold_with_same_iterator() {
let result = call_vm!(vm, "", script, "", ""); let result = call_vm!(vm, "", script, "", "");
assert_eq!(result.ret_code, 1009); assert_eq!(result.ret_code, 1008);
} }
#[test] #[test]

View File

@ -192,11 +192,11 @@ fn match_without_xor() {
let result = call_vm!(set_variable_vm, "", &script, "", ""); let result = call_vm!(set_variable_vm, "", &script, "", "");
let result = call_vm!(vm, "", &script, "", result.data); 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); let result = call_vm!(vm, "", script, "", result.data);
assert_eq!(result.ret_code, 1012); assert_eq!(result.ret_code, 1011);
} }
#[test] #[test]

View File

@ -143,11 +143,11 @@ fn mismatch_without_xor() {
let result = call_vm!(set_variable_vm, "asd", &script, "", ""); let result = call_vm!(set_variable_vm, "asd", &script, "", "");
let result = call_vm!(vm, "asd", &script, "", result.data); 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); let result = call_vm!(vm, "asd", script, "", result.data);
assert_eq!(result.ret_code, 1013); assert_eq!(result.ret_code, 1012);
} }
#[test] #[test]

View File

@ -140,7 +140,7 @@ fn par_early_exit() {
]; ];
let setter_3_malicious_data = raw_data_from_trace(setter_3_malicious_trace); 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); 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 actual_trace = trace_from_result(&init_result_3);
let expected_trace = trace_from_result(&init_result_2); let expected_trace = trace_from_result(&init_result_2);