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