mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 15:20:16 +00:00
Improve the scalars storing scheme (#162)
This commit is contained in:
parent
1c55d34981
commit
18f4c0036f
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -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"
|
||||
|
@ -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(|_| ())
|
||||
|
@ -21,7 +21,7 @@ pub(super) fn apply_to_arg(
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
should_touch_trace: bool,
|
||||
) -> ExecutionResult<ResolvedCallResult> {
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
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<ResolvedCallResult> {
|
||||
use crate::execution_step::ExecutionError::VariableNotFound;
|
||||
use crate::execution_step::Scalar;
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
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<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 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<ResolvedCallResult> {
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
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<ResolvedCallResult> {
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
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)
|
||||
}
|
||||
|
@ -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<CallResult> {
|
||||
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<'_>,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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(()),
|
||||
}
|
||||
}
|
||||
|
@ -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<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)]
|
||||
@ -45,7 +41,6 @@ impl<'i> FoldState<'i> {
|
||||
iterable,
|
||||
iterable_type,
|
||||
instr_head,
|
||||
met_variables: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
@ -88,19 +88,18 @@ fn create_scalar_iterable<'ctx>(
|
||||
exec_ctx: &ExecutionCtx<'ctx>,
|
||||
variable_name: &str,
|
||||
) -> ExecutionResult<FoldIterableScalar> {
|
||||
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<FoldIterableScalar> {
|
||||
fn from_call_result(call_result: ValueAggregate) -> ExecutionResult<FoldIterableScalar> {
|
||||
use ExecutionError::IncompatibleJValueType;
|
||||
|
||||
let len = match &call_result.result.deref() {
|
||||
@ -128,19 +127,18 @@ fn create_scalar_lambda_iterable<'ctx>(
|
||||
) -> ExecutionResult<FoldIterableScalar> {
|
||||
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())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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(())
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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()))?;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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<ResolvedCallResult>,
|
||||
pub(crate) call_results: Vec<ValueAggregate>,
|
||||
pub(crate) cursor: usize,
|
||||
}
|
||||
|
||||
impl IterableVecResolvedCall {
|
||||
pub(crate) fn init(call_results: Vec<ResolvedCallResult>) -> Self {
|
||||
pub(crate) fn init(call_results: Vec<ValueAggregate>) -> 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,
|
||||
|
@ -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;
|
||||
|
@ -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<ResolvedCallResult>> {
|
||||
impl JValuable for std::cell::Ref<'_, Vec<ValueAggregate>> {
|
||||
fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<Vec<&JValue>> {
|
||||
let stream_iter = self.iter().map(|r| r.result.deref());
|
||||
let select_result = select_from_stream(stream_iter, lambda)?;
|
||||
|
@ -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<Vec<&JValue>> {
|
||||
let selected_value = select(&self.result, lambda.iter())?;
|
||||
Ok(vec![selected_value])
|
||||
|
@ -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;
|
||||
|
@ -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<JValue>,
|
||||
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<dyn JValuable + 'ctx> {
|
||||
impl<'i> ScalarRef<'i> {
|
||||
pub(crate) fn into_jvaluable(self) -> Box<dyn JValuable + 'i> {
|
||||
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<JValue>, 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())?;
|
||||
}
|
||||
|
@ -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<Vec<ResolvedCallResult>>);
|
||||
pub(crate) struct Stream(Vec<Vec<ValueAggregate>>);
|
||||
|
||||
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<u32> {
|
||||
pub(crate) fn add_value(&mut self, value: ValueAggregate, generation: Generation) -> ExecutionResult<u32> {
|
||||
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<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) => 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<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) => 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<dyn Iterator<Item = &'result ResolvedCallResult> + 'result>,
|
||||
iter: Box<dyn Iterator<Item = &'result ValueAggregate> + 'result>,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<'result> Iterator for StreamIter<'result> {
|
||||
type Item = &'result ResolvedCallResult;
|
||||
type Item = &'result ValueAggregate;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<dyn Iterator<Item = &'slice [ResolvedCallResult]> + 'slice>,
|
||||
iter: Box<dyn Iterator<Item = &'slice [ValueAggregate]> + 'slice>,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<'slice> Iterator for StreamSliceIter<'slice> {
|
||||
type Item = &'slice [ResolvedCallResult];
|
||||
type Item = &'slice [ValueAggregate];
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.len > 0 {
|
||||
|
@ -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")]
|
||||
|
@ -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<String>) to avoid copying.
|
||||
pub scalars: HashMap<String, Scalar<'i>>,
|
||||
pub(crate) scalars: Scalars<'i>,
|
||||
|
||||
/// Contains all streams.
|
||||
// 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.
|
||||
pub next_peer_pks: Vec<String>,
|
||||
pub(crate) next_peer_pks: Vec<String>,
|
||||
|
||||
/// 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.
|
||||
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<LastErrorDescriptor>,
|
||||
pub(crate) last_error: Option<LastErrorDescriptor>,
|
||||
|
||||
/// 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)?;
|
||||
|
@ -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::*;
|
||||
|
229
air/src/execution_step/execution_context/scalar_variables.rs
Normal file
229
air/src/execution_step/execution_context/scalar_variables.rs
Normal 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(())
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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<Box<dyn JValuable + 'ctx>> {
|
||||
use ExecutionError::VariableNotFound;
|
||||
|
||||
let value = ctx
|
||||
.scalars
|
||||
.get(name)
|
||||
.ok_or_else(|| VariableNotFound(name.to_string()))?;
|
||||
|
||||
Ok(value.to_jvaluable())
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user