mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 07:10:18 +00:00
Introduce shadowing variables in fold (#33)
This commit is contained in:
parent
dde12e2a40
commit
48519be208
37
Cargo.lock
generated
37
Cargo.lock
generated
@ -121,9 +121,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.12.3"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
@ -426,7 +426,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -437,7 +437,7 @@ checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -448,23 +448,12 @@ checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"const_fn",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 0.1.10",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.0"
|
||||
@ -1180,9 +1169,9 @@ checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.2"
|
||||
version = "11.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
@ -1407,7 +1396,7 @@ checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
@ -1458,14 +1447,14 @@ checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19"
|
||||
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq",
|
||||
"crossbeam-utils 0.7.2",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1659,9 +1648,9 @@ checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.48"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
|
||||
checksum = "3b4f34193997d92804d359ed09953e25d5138df6bcc055a71bf68ee89fdf9223"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -39,9 +39,34 @@ pub(super) fn set_local_call_result<'i>(
|
||||
|
||||
match output {
|
||||
CallOutput::Scalar(name) => {
|
||||
if let Some(fold_block_name) = exec_ctx.met_folds.back() {
|
||||
let fold_state = match exec_ctx.data_cache.get_mut(*fold_block_name) {
|
||||
Some(AValue::JValueFoldCursor(fold_state)) => fold_state,
|
||||
_ => unreachable!("fold block data must be represented as fold cursor"),
|
||||
};
|
||||
|
||||
fold_state.met_variables.insert(name, result.clone());
|
||||
}
|
||||
|
||||
match exec_ctx.data_cache.entry(name.to_string()) {
|
||||
Vacant(entry) => entry.insert(AValue::JValueRef(result)),
|
||||
Occupied(entry) => return Err(MultipleVariablesFound(entry.key().clone())),
|
||||
Vacant(entry) => {
|
||||
entry.insert(AValue::JValueRef(result));
|
||||
}
|
||||
Occupied(mut entry) => {
|
||||
// check that current execution flow is inside a fold block
|
||||
if exec_ctx.met_folds.is_empty() {
|
||||
// shadowing is allowed only inside fold blocks
|
||||
return Err(MultipleVariablesFound(entry.key().clone()));
|
||||
}
|
||||
|
||||
match entry.get() {
|
||||
AValue::JValueRef(_) => {}
|
||||
// shadowing is allowed only for scalar values
|
||||
_ => return Err(ShadowingError(entry.key().clone())),
|
||||
};
|
||||
|
||||
entry.insert(AValue::JValueRef(result));
|
||||
}
|
||||
};
|
||||
}
|
||||
CallOutput::Accumulator(name) => {
|
||||
|
@ -17,6 +17,7 @@
|
||||
use crate::AValue;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
|
||||
@ -42,6 +43,9 @@ pub(crate) struct ExecutionCtx<'i> {
|
||||
/// - all of seq subtrees are complete
|
||||
/// - call executes successfully (call evidence equals to Executed)
|
||||
pub subtree_complete: bool,
|
||||
|
||||
/// List of met folds used to determine whether a variable can be shadowed.
|
||||
pub met_folds: VecDeque<&'i str>,
|
||||
}
|
||||
|
||||
impl<'i> ExecutionCtx<'i> {
|
||||
@ -52,6 +56,7 @@ impl<'i> ExecutionCtx<'i> {
|
||||
current_peer_id,
|
||||
init_peer_id,
|
||||
subtree_complete: true,
|
||||
met_folds: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ use crate::Result;
|
||||
|
||||
use air_parser::ast::{Fold, Next};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
/*
|
||||
@ -43,6 +44,8 @@ pub(crate) struct FoldState<'i> {
|
||||
pub(crate) cursor: usize,
|
||||
pub(crate) iterable: Rc<JValue>,
|
||||
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, Rc<JValue>>,
|
||||
}
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for Fold<'i> {
|
||||
@ -71,6 +74,7 @@ impl<'i> super::ExecutableInstruction<'i> for Fold<'i> {
|
||||
// TODO: reuse existing Rc from JValueRef, if there was some
|
||||
iterable: Rc::new(iterable),
|
||||
instr_head: self.instruction.clone(),
|
||||
met_variables: HashMap::new(),
|
||||
};
|
||||
|
||||
let previous_value = exec_ctx
|
||||
@ -80,9 +84,33 @@ impl<'i> super::ExecutableInstruction<'i> for Fold<'i> {
|
||||
if previous_value.is_some() {
|
||||
return Err(MultipleFoldStates(self.iterator.to_string()));
|
||||
}
|
||||
exec_ctx.met_folds.push_back(self.iterator);
|
||||
|
||||
self.instruction.execute(exec_ctx, call_ctx)?;
|
||||
exec_ctx.data_cache.remove(self.iterator);
|
||||
|
||||
let fold_state = match exec_ctx.data_cache.remove(self.iterator) {
|
||||
Some(AValue::JValueFoldCursor(fold_state)) => fold_state,
|
||||
_ => unreachable!("fold cursor is changed only inside fold block"),
|
||||
};
|
||||
|
||||
for (variable_name, _) in fold_state.met_variables {
|
||||
exec_ctx.data_cache.remove(variable_name);
|
||||
}
|
||||
exec_ctx.met_folds.pop_back();
|
||||
|
||||
if let Some(fold_block_name) = exec_ctx.met_folds.back() {
|
||||
let fold_state = match exec_ctx.data_cache.get(*fold_block_name) {
|
||||
Some(AValue::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(), AValue::JValueRef(variable.clone()));
|
||||
}
|
||||
|
||||
exec_ctx.data_cache.extend(upper_fold_values);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -138,12 +166,13 @@ mod tests {
|
||||
use crate::call_evidence::CallEvidencePath;
|
||||
use crate::JValue;
|
||||
|
||||
use aqua_test_utils::call_vm;
|
||||
use aqua_test_utils::create_aqua_vm;
|
||||
use aqua_test_utils::echo_number_call_service;
|
||||
use aqua_test_utils::set_variable_call_service;
|
||||
use aqua_test_utils::{call_vm, echo_string_call_service};
|
||||
use aquamarine_vm::AquamarineVMError;
|
||||
use aquamarine_vm::StepperError;
|
||||
use aquamarine_vm::StepperOutcome;
|
||||
|
||||
use serde_json::json;
|
||||
use std::rc::Rc;
|
||||
@ -370,4 +399,154 @@ mod tests {
|
||||
assert_eq!(res[i], Call(Executed(Rc::new(JValue::Number(i.into())))));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadowing() {
|
||||
use crate::call_evidence::CallResult::*;
|
||||
use crate::call_evidence::EvidenceState::*;
|
||||
|
||||
let mut set_variables_vm = create_aqua_vm(set_variable_call_service(r#"["1","2"]"#), "set_variable");
|
||||
let mut vm_a = create_aqua_vm(echo_string_call_service(), "A");
|
||||
let mut vm_b = create_aqua_vm(echo_string_call_service(), "B");
|
||||
|
||||
let script = String::from(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "set_variable" ("" "") [] Iterable1)
|
||||
(call "set_variable" ("" "") [] Iterable2)
|
||||
)
|
||||
(fold Iterable1 i
|
||||
(seq
|
||||
(seq
|
||||
(fold Iterable2 j
|
||||
(seq
|
||||
(seq
|
||||
(call "A" ("" "") [i] local_j)
|
||||
(call "B" ("" "") [local_j])
|
||||
)
|
||||
(next j)
|
||||
)
|
||||
)
|
||||
(par
|
||||
(call "A" ("" "") [i] local_i)
|
||||
(call "B" ("" "") [i])
|
||||
)
|
||||
)
|
||||
(next i)
|
||||
)
|
||||
)
|
||||
)"#,
|
||||
);
|
||||
|
||||
let res = call_vm!(set_variables_vm, "", script.clone(), "[]", "[]");
|
||||
let res = call_vm!(vm_a, "", script.clone(), "[]", res.data);
|
||||
let res = call_vm!(vm_b, "", script.clone(), "[]", res.data);
|
||||
let res = call_vm!(vm_a, "", script.clone(), "[]", res.data);
|
||||
let res = call_vm!(vm_b, "", script.clone(), "[]", res.data);
|
||||
let res = call_vm!(vm_a, "", script.clone(), "[]", res.data);
|
||||
let res = call_vm!(vm_b, "", script, "[]", res.data);
|
||||
|
||||
let res: CallEvidencePath = serde_json::from_str(&res.data).expect("should be valid call evidence path");
|
||||
|
||||
assert_eq!(res.len(), 12);
|
||||
for i in 2..11 {
|
||||
assert!(matches!(res[i], Call(Executed(_))) || matches!(res[i], Par(..)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadowing_scope() {
|
||||
use crate::call_evidence::CallResult::*;
|
||||
use crate::call_evidence::EvidenceState::*;
|
||||
|
||||
fn execute_script(script: String) -> Result<StepperOutcome, AquamarineVMError> {
|
||||
let mut set_variables_vm = create_aqua_vm(set_variable_call_service(r#"["1","2"]"#), "set_variable");
|
||||
let mut vm_a = create_aqua_vm(echo_string_call_service(), "A");
|
||||
let mut vm_b = create_aqua_vm(echo_string_call_service(), "B");
|
||||
|
||||
let res = call_vm!(set_variables_vm, "", script.clone(), "[]", "[]");
|
||||
let res = call_vm!(vm_a, "", script.clone(), "[]", res.data);
|
||||
let res = call_vm!(vm_b, "", script.clone(), "[]", res.data);
|
||||
let res = call_vm!(vm_a, "", script.clone(), "[]", res.data);
|
||||
let res = call_vm!(vm_b, "", script.clone(), "[]", res.data);
|
||||
|
||||
vm_a.call_with_prev_data("", script, "[]", res.data)
|
||||
}
|
||||
|
||||
let use_non_exist_variable_script = String::from(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "set_variable" ("" "") [] Iterable1)
|
||||
(call "set_variable" ("" "") [] Iterable2)
|
||||
)
|
||||
(fold Iterable1 i
|
||||
(seq
|
||||
(seq
|
||||
(fold Iterable2 j
|
||||
(seq
|
||||
(seq
|
||||
(call "A" ("" "") [i] local_j)
|
||||
(call "B" ("" "") [local_j])
|
||||
)
|
||||
(next j)
|
||||
)
|
||||
)
|
||||
(call "A" ("" "") [local_j])
|
||||
)
|
||||
(next i)
|
||||
)
|
||||
)
|
||||
)"#,
|
||||
);
|
||||
|
||||
let res = execute_script(use_non_exist_variable_script);
|
||||
assert!(res.is_err());
|
||||
let error = res.err().unwrap();
|
||||
let error = match error {
|
||||
AquamarineVMError::StepperError(error) => error,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
assert!(matches!(error, StepperError::VariableNotFound(_)));
|
||||
|
||||
let variable_shadowing_script = String::from(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "set_variable" ("" "") [] Iterable1)
|
||||
(call "set_variable" ("" "") [] Iterable2)
|
||||
)
|
||||
(fold Iterable1 i
|
||||
(seq
|
||||
(seq
|
||||
(call "A" ("" "") ["value"] local_j)
|
||||
(seq
|
||||
(fold Iterable2 j
|
||||
(seq
|
||||
(seq
|
||||
(call "A" ("" "") [i] local_j)
|
||||
(call "B" ("" "") [local_j])
|
||||
)
|
||||
(next j)
|
||||
)
|
||||
)
|
||||
(call "A" ("" "") [local_j])
|
||||
)
|
||||
)
|
||||
(next i)
|
||||
)
|
||||
)
|
||||
)"#,
|
||||
);
|
||||
|
||||
let res = execute_script(variable_shadowing_script).unwrap();
|
||||
let res: CallEvidencePath = serde_json::from_str(&res.data).expect("should be valid call evidence path");
|
||||
|
||||
assert_eq!(res.len(), 11);
|
||||
for i in 0..10 {
|
||||
assert!(matches!(res[i], Call(Executed(_))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ pub(crate) fn require_string(value: JValue) -> Result<String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply_json_path<'i>(jvalue: JValue, json_path: &'i str) -> Result<JValue> {
|
||||
pub(crate) fn apply_json_path(jvalue: JValue, json_path: &str) -> Result<JValue> {
|
||||
let values = find_by_json_path(&jvalue, json_path)?;
|
||||
if values.is_empty() {
|
||||
return Err(AquamarineError::VariableNotFound(json_path.to_string()));
|
||||
|
@ -88,6 +88,9 @@ pub enum AquamarineError {
|
||||
|
||||
/// Errors occurred when evidence path contains less elements then corresponding Par has.
|
||||
EvidencePathTooSmall(usize, usize),
|
||||
|
||||
/// Errors occurred when evidence path contains less elements then corresponding Par has.
|
||||
ShadowingError(String),
|
||||
}
|
||||
|
||||
impl Error for AquamarineError {}
|
||||
@ -167,6 +170,11 @@ impl std::fmt::Display for AquamarineError {
|
||||
"evidence path remains {} elements, but {} requires by Par",
|
||||
actual_count, desired_count
|
||||
),
|
||||
AquamarineError::ShadowingError(variable_name) => write!(
|
||||
f,
|
||||
"vairable with name = '{}' can't be shadowed, shadowing is supported only for scalar values",
|
||||
variable_name
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,6 +208,7 @@ impl Into<StepperOutcome> for AquamarineError {
|
||||
AquamarineError::IncompatibleEvidenceStates(..) => 18,
|
||||
AquamarineError::IncompatibleCallResults(..) => 19,
|
||||
AquamarineError::EvidencePathTooSmall(..) => 20,
|
||||
AquamarineError::ShadowingError(_) => 21,
|
||||
};
|
||||
|
||||
StepperOutcome {
|
||||
|
@ -46,7 +46,7 @@ pub(super) fn prepare<'i>(
|
||||
let prev_path = to_evidence_path(raw_prev_path)?;
|
||||
let path = to_evidence_path(raw_path)?;
|
||||
|
||||
let aqua: Instruction<'i> = *air_parser::parse(raw_aqua).map_err(|msg| AquamarineError::AIRParseError(msg))?;
|
||||
let aqua: Instruction<'i> = *air_parser::parse(raw_aqua).map_err(AquamarineError::AIRParseError)?;
|
||||
|
||||
log::trace!(
|
||||
target: RUN_PARAMS,
|
||||
|
Loading…
Reference in New Issue
Block a user