mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 07:10:18 +00:00
Introduce CRDT data (#74)
This commit is contained in:
parent
e805a861e2
commit
3c86d36566
4
.github/workflows/publish_crates.yml
vendored
4
.github/workflows/publish_crates.yml
vendored
@ -29,12 +29,12 @@ jobs:
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: nightly-2021-05-16
|
||||
profile: minimal
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: nightly-2021-05-16
|
||||
command: update
|
||||
args: --aggressive
|
||||
|
||||
|
6
.github/workflows/publish_interpreter.yml
vendored
6
.github/workflows/publish_interpreter.yml
vendored
@ -32,19 +32,19 @@ jobs:
|
||||
- name: Install Rust toolchain with wasm32-unknown-unknown
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: nightly-2021-05-16
|
||||
target: wasm32-unknown-unknown
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: Install wasm32-wasi
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: nightly-2021-05-16
|
||||
target: wasm32-wasi
|
||||
profile: minimal
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: nightly-2021-05-16
|
||||
command: update
|
||||
args: --aggressive
|
||||
|
||||
|
@ -41,19 +41,19 @@ jobs:
|
||||
- name: Install Rust toolchain with wasm32-unknown-unknown
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: nightly-2021-05-16
|
||||
target: wasm32-unknown-unknown
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: Install wasm32-wasi
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: nightly-2021-05-16
|
||||
target: wasm32-wasi
|
||||
profile: minimal
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: nightly-2021-05-16
|
||||
command: update
|
||||
args: --aggressive
|
||||
|
||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,3 +1,14 @@
|
||||
## Version 0.14.0 (2021-08-24)
|
||||
|
||||
[PR 74](https://github.com/fluencelabs/aquavm/pull/74):
|
||||
- introduced a new CRDT-like data format for streams:
|
||||
- call results contains different values for streams and scalars
|
||||
- introduced a new state for fold whose iterables are streams
|
||||
- merging scheme was rewritten, and became lazy
|
||||
- refactor the internal value mechanism
|
||||
- introduced a new instruction `(ap` responsible for applying json path to scalars and save results as a new scalar or add it to a stream. In the second case it'll produce a new state in a data.
|
||||
- introduced a new string literal `[]` represents empty array
|
||||
|
||||
## Version 0.10.8 (2021-07-06)
|
||||
|
||||
- improve the error message of the invalid executed state error ([PR 121](https://github.com/fluencelabs/aquavm/pull/121))
|
||||
|
42
Cargo.lock
generated
42
Cargo.lock
generated
@ -13,8 +13,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "air"
|
||||
version = "0.10.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"air-interpreter-data",
|
||||
"air-interpreter-interface",
|
||||
"air-parser",
|
||||
"air-test-utils",
|
||||
@ -38,7 +39,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "air-interpreter"
|
||||
version = "0.10.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"air",
|
||||
"log",
|
||||
@ -48,6 +49,16 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "air-interpreter-data"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"semver 1.0.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "air-interpreter-interface"
|
||||
version = "0.6.0"
|
||||
@ -319,18 +330,18 @@ checksum = "475bd7aa7680b4ed8f6bb59745e882bcbaeb39326532bb79ffb1716480d9a274"
|
||||
|
||||
[[package]]
|
||||
name = "codespan"
|
||||
version = "0.9.5"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ebaf6bb6a863ad6aa3a18729e9710c53d75df03306714d9cc1f7357a00cd789"
|
||||
checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e"
|
||||
dependencies = [
|
||||
"codespan-reporting",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.9.5"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e0762455306b1ed42bc651ef6a2197aabda5e1d4a43c34d5eab5c1a3634e81d"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
@ -930,9 +941,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
@ -1015,9 +1026,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.99"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
|
||||
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@ -1227,9 +1238,9 @@ checksum = "8dc5838acba84ce4d802d672afd0814fae0ae7098021ae5b06d975e70d09f812"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "memmap"
|
||||
@ -1697,6 +1708,9 @@ name = "semver"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
@ -1812,9 +1826,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.74"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
|
||||
checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -5,6 +5,7 @@ members = [
|
||||
"crates/air-parser",
|
||||
"crates/polyplets",
|
||||
"crates/interpreter-interface",
|
||||
"crates/interpreter-data",
|
||||
"crates/test-module",
|
||||
"crates/test-utils",
|
||||
"avm/server",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "air-interpreter"
|
||||
version = "0.10.0"
|
||||
version = "0.14.0"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
@ -24,7 +24,7 @@ wasm-bindgen = "=0.2.65"
|
||||
|
||||
log = "0.4.11"
|
||||
serde = { version = "=1.0.118", features = [ "derive", "rc" ] }
|
||||
serde_json = "1.0"
|
||||
serde_json = "=1.0.61"
|
||||
|
||||
[features]
|
||||
marine = ["air/marine"]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "air"
|
||||
version = "0.10.0"
|
||||
version = "0.14.0"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
@ -13,6 +13,7 @@ doctest = false
|
||||
[dependencies]
|
||||
air-parser = { path = "../crates/air-parser" }
|
||||
polyplets = { path = "../crates/polyplets" }
|
||||
air-interpreter-data = { path = "../crates/interpreter-data" }
|
||||
air-interpreter-interface = { path = "../crates/interpreter-interface" }
|
||||
marine-rs-sdk = { version = "0.6.11", features = ["logger"] }
|
||||
|
||||
@ -38,7 +39,7 @@ once_cell = "1.4.1"
|
||||
env_logger = "0.7.1"
|
||||
maplit = "1.0.2"
|
||||
pretty_assertions = "0.6.1"
|
||||
serde_json = "1.0.56"
|
||||
serde_json = "=1.0.61"
|
||||
|
||||
[[bench]]
|
||||
name = "call_benchmark"
|
||||
|
@ -19,7 +19,7 @@ thread_local!(static SCRIPT: String = String::from(
|
||||
);
|
||||
|
||||
fn current_peer_id_call() -> Result<InterpreterOutcome, AVMError> {
|
||||
VM.with(|vm| SCRIPT.with(|script| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", "[]")))
|
||||
VM.with(|vm| SCRIPT.with(|script| vm.borrow_mut().call_with_prev_data("", script.clone(), "", "")))
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
@ -55,26 +55,26 @@ fn chat_sent_message_benchmark() -> Result<InterpreterOutcome, AVMError> {
|
||||
"#,
|
||||
);
|
||||
|
||||
let res = CLIENT_1_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", "[]"))
|
||||
let result = CLIENT_1_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", ""))
|
||||
.unwrap();
|
||||
let res = RELAY_1_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res.data))
|
||||
let result = RELAY_1_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", result.data))
|
||||
.unwrap();
|
||||
let res = REMOTE_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res.data))
|
||||
let result = REMOTE_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", result.data))
|
||||
.unwrap();
|
||||
let res_data = res.data.clone();
|
||||
let res_data = result.data.clone();
|
||||
let res1 = RELAY_1_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res_data))
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", res_data))
|
||||
.unwrap();
|
||||
CLIENT_1_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res1.data))
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", res1.data))
|
||||
.unwrap();
|
||||
let res2 = RELAY_2_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res.data))
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", result.data))
|
||||
.unwrap();
|
||||
CLIENT_2_VM.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res2.data))
|
||||
CLIENT_2_VM.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", res2.data))
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
@ -92,10 +92,10 @@ fn create_service_benchmark() -> Result<InterpreterOutcome, AVMError> {
|
||||
)"#,
|
||||
);
|
||||
|
||||
let res = SET_VARIABLES_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", "[]"))
|
||||
let result = SET_VARIABLES_VM
|
||||
.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", ""))
|
||||
.unwrap();
|
||||
VM.with(|vm| vm.borrow_mut().call_with_prev_data("", script, "[]", res.data))
|
||||
VM.with(|vm| vm.borrow_mut().call_with_prev_data("", script, "", result.data))
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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::execution::ExecutionError;
|
||||
use crate::preparation::PreparationError;
|
||||
|
||||
use crate::InterpreterOutcome;
|
||||
use crate::INTERPRETER_SUCCESS;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
|
||||
const EXECUTION_ERRORS_START_ID: i32 = 1000;
|
||||
|
||||
/// Create InterpreterOutcome from supplied data and next_peer_pks,
|
||||
/// set ret_code to INTERPRETER_SUCCESS.
|
||||
pub(crate) fn from_path_and_peers<T>(data: &T, next_peer_pks: Vec<String>) -> InterpreterOutcome
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let data = serde_json::to_vec(data).expect("default serializer shouldn't fail");
|
||||
let next_peer_pks = dedup(next_peer_pks);
|
||||
|
||||
InterpreterOutcome {
|
||||
ret_code: INTERPRETER_SUCCESS,
|
||||
error_message: String::new(),
|
||||
data,
|
||||
next_peer_pks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create InterpreterOutcome from supplied data and error,
|
||||
/// set ret_code based on the error.
|
||||
pub(crate) fn from_preparation_error(data: impl Into<Vec<u8>>, err: PreparationError) -> InterpreterOutcome {
|
||||
let ret_code = err.to_error_code() as i32;
|
||||
let data = data.into();
|
||||
|
||||
InterpreterOutcome {
|
||||
ret_code,
|
||||
error_message: format!("{}", err),
|
||||
data,
|
||||
next_peer_pks: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Create InterpreterOutcome from supplied data, next_peer_pks and error,
|
||||
/// set ret_code based on the error.
|
||||
pub(crate) fn from_execution_error<T>(
|
||||
data: &T,
|
||||
next_peer_pks: Vec<String>,
|
||||
err: Rc<ExecutionError>,
|
||||
) -> InterpreterOutcome
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let ret_code = err.to_error_code() as i32;
|
||||
let ret_code = EXECUTION_ERRORS_START_ID + ret_code;
|
||||
|
||||
let data = serde_json::to_vec(data).expect("default serializer shouldn't fail");
|
||||
let next_peer_pks = dedup(next_peer_pks);
|
||||
|
||||
InterpreterOutcome {
|
||||
ret_code,
|
||||
error_message: format!("{}", err),
|
||||
data,
|
||||
next_peer_pks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Deduplicate values in a supplied vector.
|
||||
fn dedup<T: Eq + Hash>(mut vec: Vec<T>) -> Vec<T> {
|
||||
use std::collections::HashSet;
|
||||
|
||||
let set: HashSet<_> = vec.drain(..).collect();
|
||||
set.into_iter().collect()
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
mod executed_state;
|
||||
|
||||
pub use executed_state::CallResult;
|
||||
pub use executed_state::ExecutedState;
|
||||
pub use executed_state::ParResult;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
|
||||
pub type ExecutionTrace = std::collections::VecDeque<ExecutedState>;
|
||||
|
||||
/// Encapsulates all necessary state regarding to the call paths.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub(crate) struct ExecutionTraceCtx {
|
||||
/// Contains trace (serialized tree of states) after merging current and previous data,
|
||||
/// interpreter used it to realize which instructions have been already executed.
|
||||
pub(crate) current_trace: ExecutionTrace,
|
||||
|
||||
/// Size of a current considered subtree inside current path.
|
||||
pub(crate) current_subtree_size: usize,
|
||||
|
||||
// TODO: consider change it to Vec for optimization
|
||||
/// Stream for resulted path produced by the interpreter after execution.
|
||||
pub(crate) new_trace: ExecutionTrace,
|
||||
}
|
||||
|
||||
impl ExecutionTraceCtx {
|
||||
pub fn new(current_trace: ExecutionTrace) -> Self {
|
||||
let current_subtree_size = current_trace.len();
|
||||
// a new execution trace will contain at least current_path.len() elements
|
||||
let new_trace = ExecutionTrace::with_capacity(current_subtree_size);
|
||||
|
||||
Self {
|
||||
current_trace,
|
||||
current_subtree_size,
|
||||
new_trace,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ExecutionTraceCtx {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "current trace:\n{:?}", self.current_trace)?;
|
||||
writeln!(f, "current subtree elements count:\n{:?}", self.current_subtree_size)?;
|
||||
writeln!(f, "new trace:\n{:?}", self.new_trace)
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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::JValue;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct ParResult(pub usize, pub usize);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CallResult {
|
||||
/// Request was sent to a target node by node with such public key and it shouldn't be called again.
|
||||
RequestSentBy(String),
|
||||
|
||||
/// A corresponding call's been already executed with such value and result.
|
||||
Executed(Rc<JValue>),
|
||||
|
||||
/// call_service ended with a service error.
|
||||
CallServiceFailed(i32, Rc<String>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecutedState {
|
||||
Par(ParResult),
|
||||
Call(CallResult),
|
||||
}
|
||||
|
||||
impl ExecutedState {
|
||||
pub fn par(left: usize, right: usize) -> Self {
|
||||
Self::Par(ParResult(left, right))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ExecutedState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use CallResult::*;
|
||||
use ExecutedState::*;
|
||||
|
||||
match self {
|
||||
Par(ParResult(left, right)) => write!(f, "Par({}, {})", left, right),
|
||||
Call(RequestSentBy(peer_id)) => write!(f, "RequestSentBy({})", peer_id),
|
||||
Call(Executed(result)) => write!(f, "Executed({:?})", result),
|
||||
Call(CallServiceFailed(ret_code, err_msg)) => write!(f, "CallServiceFailed({}, {})", ret_code, err_msg),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
mod execution_context;
|
||||
mod execution_trace_context;
|
||||
|
||||
pub mod execution_trace {
|
||||
pub use super::execution_trace_context::CallResult;
|
||||
pub use super::execution_trace_context::ExecutedState;
|
||||
pub use super::execution_trace_context::ExecutionTrace;
|
||||
pub(crate) use super::execution_trace_context::ExecutionTraceCtx;
|
||||
pub use super::execution_trace_context::ParResult;
|
||||
}
|
||||
|
||||
pub(crate) mod execution {
|
||||
pub use super::execution_context::error_descriptor::LastError;
|
||||
pub(crate) use super::execution_context::error_descriptor::LastErrorDescriptor;
|
||||
pub(crate) use super::execution_context::error_descriptor::LastErrorWithTetraplets;
|
||||
pub(crate) use super::execution_context::AValue;
|
||||
pub(crate) use super::execution_context::ExecutionCtx;
|
||||
pub(crate) use super::execution_context::ResolvedCallResult;
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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::Call;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use crate::contexts::execution::ResolvedCallResult;
|
||||
use crate::contexts::execution_trace::*;
|
||||
use crate::exec_err;
|
||||
use crate::execution::Variable;
|
||||
use crate::log_targets::EXECUTED_STATE_CHANGING;
|
||||
use crate::JValue;
|
||||
|
||||
use air_parser::ast::CallOutputValue;
|
||||
use polyplets::ResolvedTriplet;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Writes result of a local `Call` instruction to `ExecutionCtx` at `output`.
|
||||
pub(super) fn set_local_call_result<'i>(
|
||||
result: Rc<JValue>,
|
||||
triplet: Rc<ResolvedTriplet>,
|
||||
output: &CallOutputValue<'i>,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<()> {
|
||||
use crate::contexts::execution::AValue;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
use ExecutionError::*;
|
||||
|
||||
let executed_result = ResolvedCallResult { result, triplet };
|
||||
|
||||
match output {
|
||||
CallOutputValue::Variable(Variable::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, executed_result.clone());
|
||||
}
|
||||
|
||||
match exec_ctx.data_cache.entry(name.to_string()) {
|
||||
Vacant(entry) => {
|
||||
entry.insert(AValue::JValueRef(executed_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 exec_err!(MultipleVariablesFound(entry.key().clone()));
|
||||
}
|
||||
|
||||
match entry.get() {
|
||||
AValue::JValueRef(_) => {}
|
||||
// shadowing is allowed only for scalar values
|
||||
_ => return exec_err!(ShadowingError(entry.key().clone())),
|
||||
};
|
||||
|
||||
entry.insert(AValue::JValueRef(executed_result));
|
||||
}
|
||||
};
|
||||
}
|
||||
CallOutputValue::Variable(Variable::Stream(name)) => {
|
||||
match exec_ctx.data_cache.entry(name.to_string()) {
|
||||
Occupied(mut entry) => match entry.get_mut() {
|
||||
// if result is an array, insert result to the end of the array
|
||||
AValue::JValueStreamRef(values) => values.borrow_mut().push(executed_result),
|
||||
v => return exec_err!(IncompatibleAValueType(format!("{}", v), String::from("Array"))),
|
||||
},
|
||||
Vacant(entry) => {
|
||||
entry.insert(AValue::JValueStreamRef(RefCell::new(vec![executed_result])));
|
||||
}
|
||||
};
|
||||
}
|
||||
CallOutputValue::None => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes an executed state of a particle being sent to remote node
|
||||
pub(super) fn set_remote_call_result<'i>(
|
||||
peer_pk: String,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
trace_ctx: &mut ExecutionTraceCtx,
|
||||
) {
|
||||
exec_ctx.next_peer_pks.push(peer_pk);
|
||||
exec_ctx.subtree_complete = false;
|
||||
|
||||
let new_executed_state = ExecutedState::Call(CallResult::RequestSentBy(exec_ctx.current_peer_id.clone()));
|
||||
log::trace!(
|
||||
target: EXECUTED_STATE_CHANGING,
|
||||
" adding new call executed state {:?}",
|
||||
new_executed_state
|
||||
);
|
||||
trace_ctx.new_trace.push_back(new_executed_state);
|
||||
}
|
||||
|
||||
/// This function looks at the existing call state, validates it,
|
||||
/// and returns Ok(true) if the call should be executed further.
|
||||
pub(super) fn handle_prev_state<'i>(
|
||||
triplet: &Rc<ResolvedTriplet>,
|
||||
output: &CallOutputValue<'i>,
|
||||
prev_state: ExecutedState,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
trace_ctx: &mut ExecutionTraceCtx,
|
||||
instruction: &Call<'i>,
|
||||
) -> ExecutionResult<bool> {
|
||||
use CallResult::*;
|
||||
use ExecutedState::*;
|
||||
|
||||
match &prev_state {
|
||||
// this call was failed on one of the previous executions,
|
||||
// here it's needed to bubble this special error up
|
||||
Call(CallServiceFailed(ret_code, err_msg)) => {
|
||||
let ret_code = *ret_code;
|
||||
let err_msg = err_msg.clone();
|
||||
trace_ctx.new_trace.push_back(prev_state);
|
||||
exec_ctx.subtree_complete = false;
|
||||
exec_err!(ExecutionError::LocalServiceError(ret_code, err_msg))
|
||||
}
|
||||
Call(RequestSentBy(..)) => {
|
||||
let peer_pk = triplet.peer_pk.as_str();
|
||||
// check whether current node can execute this call
|
||||
let is_current_peer = peer_pk == exec_ctx.current_peer_id;
|
||||
if is_current_peer {
|
||||
Ok(true)
|
||||
} else {
|
||||
exec_ctx.subtree_complete = false;
|
||||
trace_ctx.new_trace.push_back(prev_state);
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
// this instruction's been already executed
|
||||
Call(Executed(result)) => {
|
||||
set_local_call_result(result.clone(), triplet.clone(), output, exec_ctx)?;
|
||||
trace_ctx.new_trace.push_back(prev_state);
|
||||
Ok(false)
|
||||
}
|
||||
// state has inconsistent order - return a error, call shouldn't be executed
|
||||
par_state @ Par(..) => exec_err!(ExecutionError::InvalidExecutedState {
|
||||
instruction: instruction.to_string(),
|
||||
expected_state: "call",
|
||||
actual_state: par_state.clone(),
|
||||
current_trace: trace_ctx.current_trace.clone(),
|
||||
new_trace: trace_ctx.new_trace.clone(),
|
||||
current_subtree_size: trace_ctx.current_subtree_size,
|
||||
}),
|
||||
}
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
mod utils;
|
||||
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::ExecutionTraceCtx;
|
||||
use super::Instruction;
|
||||
use crate::contexts::execution::AValue;
|
||||
use crate::contexts::execution::ResolvedCallResult;
|
||||
use crate::exec_err;
|
||||
use crate::execution::boxed_value::*;
|
||||
use crate::log_instruction;
|
||||
|
||||
use air_parser::ast::Fold;
|
||||
use air_parser::ast::Next;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use utils::IterableValue;
|
||||
|
||||
pub(crate) struct FoldState<'i> {
|
||||
pub(crate) iterable: IterableValue,
|
||||
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>,
|
||||
}
|
||||
|
||||
impl<'i> FoldState<'i> {
|
||||
pub fn new(iterable: IterableValue, instr_head: Rc<Instruction<'i>>) -> Self {
|
||||
Self {
|
||||
iterable,
|
||||
instr_head,
|
||||
met_variables: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for Fold<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
use ExecutionError::MultipleFoldStates;
|
||||
|
||||
log_instruction!(fold, exec_ctx, trace_ctx);
|
||||
|
||||
let iterable = match utils::construct_iterable_value(&self.iterable, exec_ctx)? {
|
||||
Some(iterable) => iterable,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let fold_state = FoldState::new(iterable, self.instruction.clone());
|
||||
|
||||
let previous_value = exec_ctx
|
||||
.data_cache
|
||||
.insert(self.iterator.to_string(), AValue::JValueFoldCursor(fold_state));
|
||||
|
||||
if previous_value.is_some() {
|
||||
return exec_err!(MultipleFoldStates(self.iterator.to_string()));
|
||||
}
|
||||
exec_ctx.met_folds.push_back(self.iterator);
|
||||
|
||||
self.instruction.execute(exec_ctx, trace_ctx)?;
|
||||
|
||||
cleanup_variables(exec_ctx, &self.iterator);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for Next<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
use ExecutionError::FoldStateNotFound;
|
||||
use ExecutionError::IncompatibleAValueType;
|
||||
|
||||
log_instruction!(next, exec_ctx, trace_ctx);
|
||||
|
||||
let iterator_name = self.0;
|
||||
let avalue = exec_ctx
|
||||
.data_cache
|
||||
.get_mut(iterator_name)
|
||||
.ok_or_else(|| FoldStateNotFound(iterator_name.to_string()))?;
|
||||
|
||||
let fold_state = match avalue {
|
||||
AValue::JValueFoldCursor(state) => state,
|
||||
v => {
|
||||
// it's not possible to use unreachable here
|
||||
// because at now next syntactically could be used without fold
|
||||
return exec_err!(IncompatibleAValueType(
|
||||
format!("{}", v),
|
||||
String::from("JValueFoldCursor"),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if !fold_state.iterable.next() {
|
||||
// just do nothing to exit
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let next_instr = fold_state.instr_head.clone();
|
||||
next_instr.execute(exec_ctx, trace_ctx)?;
|
||||
|
||||
// get the same fold state again because of borrow checker
|
||||
match exec_ctx.data_cache.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(AValue::JValueFoldCursor(fold_state)) => fold_state.iterable.prev(),
|
||||
_ => unreachable!("iterator value shouldn't changed inside fold"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn cleanup_variables(exec_ctx: &mut ExecutionCtx<'_>, iterator: &str) {
|
||||
let fold_state = match exec_ctx.data_cache.remove(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();
|
||||
|
||||
// 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.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);
|
||||
}
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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::*;
|
||||
use crate::exec_err;
|
||||
use crate::execution::utils::get_variable_name;
|
||||
use crate::JValue;
|
||||
use crate::ResolvedTriplet;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_parser::ast;
|
||||
use jsonpath_lib::select;
|
||||
use jsonpath_lib::select_with_iter;
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(super) type IterableValue = Box<dyn for<'ctx> Iterable<'ctx, Item = IterableItem<'ctx>>>;
|
||||
|
||||
/// Constructs iterable value for given instruction value,
|
||||
/// return Some if iterable isn't empty and None otherwise.
|
||||
pub(super) fn construct_iterable_value<'ctx>(
|
||||
ast_iterable: &ast::IterableValue<'ctx>,
|
||||
exec_ctx: &ExecutionCtx<'ctx>,
|
||||
) -> ExecutionResult<Option<IterableValue>> {
|
||||
match ast_iterable {
|
||||
ast::IterableValue::Variable(variable) => {
|
||||
let name = get_variable_name(variable);
|
||||
handle_instruction_variable(exec_ctx, name)
|
||||
}
|
||||
ast::IterableValue::JsonPath {
|
||||
variable,
|
||||
path,
|
||||
should_flatten,
|
||||
} => {
|
||||
let name = get_variable_name(variable);
|
||||
handle_instruction_json_path(exec_ctx, name, path, *should_flatten)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_instruction_variable<'ctx>(
|
||||
exec_ctx: &ExecutionCtx<'ctx>,
|
||||
variable_name: &str,
|
||||
) -> ExecutionResult<Option<IterableValue>> {
|
||||
let iterable: Option<IterableValue> = match exec_ctx.data_cache.get(variable_name) {
|
||||
Some(AValue::JValueRef(call_result)) => from_call_result(call_result.clone())?,
|
||||
Some(AValue::JValueStreamRef(stream)) => {
|
||||
let stream = stream.borrow();
|
||||
if stream.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let call_results = stream.iter().cloned().collect::<Vec<_>>();
|
||||
let foldable = IterableVecResolvedCall::init(call_results);
|
||||
Some(Box::new(foldable))
|
||||
}
|
||||
Some(AValue::JValueFoldCursor(fold_state)) => {
|
||||
let iterable_value = fold_state.iterable.peek().unwrap();
|
||||
let jvalue = iterable_value.as_jvalue();
|
||||
let result = Rc::new(jvalue.into_owned());
|
||||
let triplet = as_triplet(&iterable_value);
|
||||
|
||||
let call_result = ResolvedCallResult { result, triplet };
|
||||
from_call_result(call_result)?
|
||||
}
|
||||
_ => return exec_err!(ExecutionError::VariableNotFound(variable_name.to_string())),
|
||||
};
|
||||
|
||||
Ok(iterable)
|
||||
}
|
||||
|
||||
/// Constructs iterable value from resolved call result.
|
||||
fn from_call_result(call_result: ResolvedCallResult) -> ExecutionResult<Option<IterableValue>> {
|
||||
use ExecutionError::IncompatibleJValueType;
|
||||
|
||||
let len = match &call_result.result.deref() {
|
||||
JValue::Array(array) => {
|
||||
if array.is_empty() {
|
||||
// skip fold if array is empty
|
||||
return Ok(None);
|
||||
}
|
||||
array.len()
|
||||
}
|
||||
v => return exec_err!(IncompatibleJValueType((*v).clone(), "array")),
|
||||
};
|
||||
|
||||
let foldable = IterableResolvedCall::init(call_result, len);
|
||||
let foldable = Box::new(foldable);
|
||||
|
||||
Ok(Some(foldable))
|
||||
}
|
||||
|
||||
fn handle_instruction_json_path<'ctx>(
|
||||
exec_ctx: &ExecutionCtx<'ctx>,
|
||||
variable_name: &str,
|
||||
json_path: &str,
|
||||
should_flatten: bool,
|
||||
) -> ExecutionResult<Option<IterableValue>> {
|
||||
use ExecutionError::JValueStreamJsonPathError;
|
||||
|
||||
match exec_ctx.data_cache.get(variable_name) {
|
||||
Some(AValue::JValueRef(variable)) => {
|
||||
let jvalues = apply_json_path(&variable.result, json_path)?;
|
||||
from_jvalues(jvalues, variable.triplet.clone(), json_path, should_flatten)
|
||||
}
|
||||
Some(AValue::JValueStreamRef(stream)) => {
|
||||
let stream = stream.borrow();
|
||||
if stream.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let acc_iter = stream.iter().map(|v| v.result.deref());
|
||||
let (jvalues, tetraplet_indices) = select_with_iter(acc_iter, &json_path)
|
||||
.map_err(|e| JValueStreamJsonPathError(stream.clone(), json_path.to_string(), e))?;
|
||||
|
||||
let jvalues = construct_iterable_jvalues(jvalues, should_flatten)?;
|
||||
let tetraplets = tetraplet_indices
|
||||
.into_iter()
|
||||
.map(|id| SecurityTetraplet {
|
||||
triplet: stream[id].triplet.clone(),
|
||||
json_path: json_path.to_string(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let foldable = IterableVecJsonPathResult::init(jvalues, tetraplets);
|
||||
Ok(Some(Box::new(foldable)))
|
||||
}
|
||||
Some(AValue::JValueFoldCursor(fold_state)) => {
|
||||
let iterable_value = fold_state.iterable.peek().unwrap();
|
||||
let jvalues = iterable_value.apply_json_path(json_path)?;
|
||||
let triplet = as_triplet(&iterable_value);
|
||||
|
||||
from_jvalues(jvalues, triplet, json_path, should_flatten)
|
||||
}
|
||||
_ => return exec_err!(ExecutionError::VariableNotFound(variable_name.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_json_path<'jvalue, 'str>(
|
||||
jvalue: &'jvalue JValue,
|
||||
json_path: &'str str,
|
||||
) -> ExecutionResult<Vec<&'jvalue JValue>> {
|
||||
use ExecutionError::JValueJsonPathError;
|
||||
|
||||
select(jvalue, json_path).map_err(|e| Rc::new(JValueJsonPathError(jvalue.clone(), json_path.to_string(), e)))
|
||||
}
|
||||
|
||||
/// Applies json_path to provided jvalues and construct IterableValue from the result and given triplet.
|
||||
fn from_jvalues(
|
||||
jvalues: Vec<&JValue>,
|
||||
triplet: Rc<ResolvedTriplet>,
|
||||
json_path: &str,
|
||||
should_flatten: bool,
|
||||
) -> ExecutionResult<Option<IterableValue>> {
|
||||
let jvalues = construct_iterable_jvalues(jvalues, should_flatten)?;
|
||||
|
||||
if jvalues.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let tetraplet = SecurityTetraplet {
|
||||
triplet,
|
||||
json_path: json_path.to_string(),
|
||||
};
|
||||
|
||||
let foldable = IterableJsonPathResult::init(jvalues, tetraplet);
|
||||
Ok(Some(Box::new(foldable)))
|
||||
}
|
||||
|
||||
fn construct_iterable_jvalues(jvalues: Vec<&JValue>, should_flatten: bool) -> ExecutionResult<Vec<JValue>> {
|
||||
if !should_flatten {
|
||||
let jvalues = jvalues.into_iter().cloned().collect();
|
||||
return Ok(jvalues);
|
||||
}
|
||||
|
||||
if jvalues.len() != 1 {
|
||||
let jvalues = jvalues.into_iter().cloned().collect();
|
||||
let jvalue = JValue::Array(jvalues);
|
||||
return exec_err!(ExecutionError::FlatteningError(jvalue));
|
||||
}
|
||||
|
||||
match jvalues[0] {
|
||||
JValue::Array(values) => Ok(values.clone()),
|
||||
_ => {
|
||||
let jvalues = jvalues.into_iter().cloned().collect();
|
||||
let jvalue = JValue::Array(jvalues);
|
||||
exec_err!(ExecutionError::FlatteningError(jvalue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_triplet(iterable: &IterableItem<'_>) -> Rc<ResolvedTriplet> {
|
||||
use IterableItem::*;
|
||||
|
||||
let tetraplet = match iterable {
|
||||
RefRef((_, tetraplet)) => tetraplet,
|
||||
RefValue((_, tetraplet)) => tetraplet,
|
||||
RcValue((_, tetraplet)) => tetraplet,
|
||||
};
|
||||
|
||||
// clone is cheap here, because triplet is under Rc
|
||||
Rc::clone(&tetraplet.triplet)
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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::ExecutableInstruction;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionResult;
|
||||
use super::ExecutionTraceCtx;
|
||||
use super::Instruction;
|
||||
use crate::contexts::execution_trace::ExecutedState;
|
||||
use crate::contexts::execution_trace::ParResult;
|
||||
use crate::log_instruction;
|
||||
use crate::log_targets::EXECUTED_STATE_CHANGING;
|
||||
|
||||
use air_parser::ast::Par;
|
||||
|
||||
enum SubtreeType {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SubtreeType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Left => write!(f, "left"),
|
||||
Self::Right => write!(f, "right"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> ExecutableInstruction<'i> for Par<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
use SubtreeType::*;
|
||||
|
||||
log_instruction!(par, exec_ctx, trace_ctx);
|
||||
|
||||
let (left_subtree_size, right_subtree_size) = extract_subtree_sizes(trace_ctx, &self)?;
|
||||
|
||||
let par_pos = trace_ctx.new_trace.len();
|
||||
trace_ctx.new_trace.push_back(ExecutedState::par(0, 0));
|
||||
|
||||
// execute a left subtree of this par
|
||||
execute_subtree(&self.0, left_subtree_size, exec_ctx, trace_ctx, par_pos, Left)?;
|
||||
let left_subtree_complete = exec_ctx.subtree_complete;
|
||||
|
||||
// execute a right subtree of this par
|
||||
execute_subtree(&self.1, right_subtree_size, exec_ctx, trace_ctx, par_pos, Right)?;
|
||||
let right_subtree_complete = exec_ctx.subtree_complete;
|
||||
|
||||
// par is completed if at least one of its subtrees is completed
|
||||
exec_ctx.subtree_complete = left_subtree_complete || right_subtree_complete;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_subtree_sizes(trace_ctx: &mut ExecutionTraceCtx, instruction: &Par<'_>) -> ExecutionResult<(usize, usize)> {
|
||||
use super::ExecutionError::InvalidExecutedState;
|
||||
|
||||
if trace_ctx.current_subtree_size == 0 {
|
||||
return Ok((0, 0));
|
||||
}
|
||||
|
||||
trace_ctx.current_subtree_size -= 1;
|
||||
|
||||
log::trace!(
|
||||
target: EXECUTED_STATE_CHANGING,
|
||||
" previous call executed state was found {:?}",
|
||||
trace_ctx.current_trace[0]
|
||||
);
|
||||
|
||||
// unwrap is safe here because of length's been checked
|
||||
match trace_ctx.current_trace.pop_front().unwrap() {
|
||||
ExecutedState::Par(ParResult(left, right)) => Ok((left, right)),
|
||||
state => crate::exec_err!(InvalidExecutedState {
|
||||
instruction: instruction.to_string(),
|
||||
expected_state: "par",
|
||||
actual_state: state,
|
||||
current_trace: trace_ctx.current_trace.clone(),
|
||||
new_trace: trace_ctx.new_trace.clone(),
|
||||
current_subtree_size: trace_ctx.current_subtree_size,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute provided subtree and update Par state in trace_ctx.new_trace.
|
||||
fn execute_subtree<'i>(
|
||||
subtree: &Instruction<'i>,
|
||||
subtree_size: usize,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
trace_ctx: &mut ExecutionTraceCtx,
|
||||
current_par_pos: usize,
|
||||
subtree_type: SubtreeType,
|
||||
) -> ExecutionResult<()> {
|
||||
use super::ExecutionError::LocalServiceError;
|
||||
|
||||
let before_subtree_size = trace_ctx.current_subtree_size;
|
||||
trace_ctx.current_subtree_size = subtree_size;
|
||||
let before_new_path_len = trace_ctx.new_trace.len();
|
||||
|
||||
exec_ctx.subtree_complete = determine_subtree_complete(&subtree);
|
||||
|
||||
// execute a subtree
|
||||
match subtree.execute(exec_ctx, trace_ctx) {
|
||||
res @ Ok(_) => {
|
||||
update_par_state(trace_ctx, subtree_type, current_par_pos, before_new_path_len);
|
||||
trace_ctx.current_subtree_size = before_subtree_size - subtree_size;
|
||||
res
|
||||
}
|
||||
// if there is a service error, update already added Par state
|
||||
// and then bubble the error up
|
||||
Err(err) if matches!(&*err, LocalServiceError(..)) => {
|
||||
update_par_state(trace_ctx, subtree_type, current_par_pos, before_new_path_len);
|
||||
trace_ctx.current_subtree_size = before_subtree_size - subtree_size;
|
||||
Err(err)
|
||||
}
|
||||
err @ Err(_) => err,
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_subtree_complete(next_instruction: &Instruction<'_>) -> bool {
|
||||
// this is needed to prevent situation when on such pattern
|
||||
// (fold (Iterable i
|
||||
// (par
|
||||
// (call ..)
|
||||
// (next i)
|
||||
// )
|
||||
// )
|
||||
// par will be completed after the last next that wouldn't change subtree_complete
|
||||
!matches!(next_instruction, Instruction::Next(_))
|
||||
}
|
||||
|
||||
/// Set left or right fields of a Par identified by current_par_pos.
|
||||
fn update_par_state(
|
||||
trace_ctx: &mut ExecutionTraceCtx,
|
||||
subtree_type: SubtreeType,
|
||||
current_par_pos: usize,
|
||||
before_new_path_len: usize,
|
||||
) {
|
||||
let new_subtree_size = trace_ctx.new_trace.len() - before_new_path_len;
|
||||
|
||||
// unwrap is safe here, because this par is added at the beginning of this par instruction.
|
||||
let par_state = trace_ctx.new_trace.get_mut(current_par_pos).unwrap();
|
||||
match par_state {
|
||||
ExecutedState::Par(ParResult(left, right)) => {
|
||||
if let SubtreeType::Left = subtree_type {
|
||||
*left = new_subtree_size;
|
||||
} else {
|
||||
*right = new_subtree_size;
|
||||
}
|
||||
|
||||
log::trace!(
|
||||
target: EXECUTED_STATE_CHANGING,
|
||||
" set {} par subtree size to {}",
|
||||
subtree_type,
|
||||
new_subtree_size
|
||||
);
|
||||
}
|
||||
_ => unreachable!("current_pas_pos must point to a par state"),
|
||||
}
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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::contexts::execution::AValue;
|
||||
use crate::contexts::execution::ExecutionCtx;
|
||||
use crate::contexts::execution::LastErrorWithTetraplets;
|
||||
use crate::execution::boxed_value::JValuable;
|
||||
use crate::execution::ExecutionError;
|
||||
use crate::execution::ExecutionResult;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_parser::ast::CallInstrArgValue;
|
||||
use air_parser::ast::LastErrorPath;
|
||||
use serde_json::json;
|
||||
|
||||
/// Resolve value to called function arguments.
|
||||
pub(crate) fn resolve_to_args<'i>(
|
||||
value: &CallInstrArgValue<'i>,
|
||||
ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
match value {
|
||||
CallInstrArgValue::InitPeerId => prepare_consts(ctx.init_peer_id.clone(), ctx),
|
||||
CallInstrArgValue::LastError(path) => prepare_last_error(path, ctx),
|
||||
CallInstrArgValue::Literal(value) => prepare_consts(value.to_string(), ctx),
|
||||
CallInstrArgValue::Boolean(value) => prepare_consts(*value, ctx),
|
||||
CallInstrArgValue::Number(value) => prepare_consts(value, ctx),
|
||||
CallInstrArgValue::Variable(variable) => prepare_variable(variable, ctx),
|
||||
CallInstrArgValue::JsonPath {
|
||||
variable,
|
||||
path,
|
||||
should_flatten,
|
||||
} => prepare_json_path(variable, path, *should_flatten, ctx),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn prepare_consts(arg: impl Into<JValue>, ctx: &ExecutionCtx<'_>) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
let jvalue = arg.into();
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.clone());
|
||||
|
||||
Ok((jvalue, vec![tetraplet]))
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn prepare_last_error(
|
||||
path: &LastErrorPath,
|
||||
ctx: &ExecutionCtx<'_>,
|
||||
) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
let LastErrorWithTetraplets { last_error, tetraplets } = ctx.last_error();
|
||||
let jvalue = match path {
|
||||
LastErrorPath::Instruction => JValue::String(last_error.instruction),
|
||||
LastErrorPath::Message => JValue::String(last_error.msg),
|
||||
LastErrorPath::PeerId => JValue::String(last_error.peer_id),
|
||||
LastErrorPath::None => json!(last_error),
|
||||
};
|
||||
|
||||
Ok((jvalue, tetraplets))
|
||||
}
|
||||
|
||||
fn prepare_variable<'i>(
|
||||
variable: &Variable<'_>,
|
||||
ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
let resolved = resolve_variable(variable, ctx)?;
|
||||
let tetraplets = resolved.as_tetraplets();
|
||||
let jvalue = resolved.into_jvalue();
|
||||
|
||||
Ok((jvalue, tetraplets))
|
||||
}
|
||||
|
||||
fn resolve_variable<'ctx, 'i>(
|
||||
variable: &Variable<'_>,
|
||||
ctx: &'ctx ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<Box<dyn JValuable + 'ctx>> {
|
||||
match variable {
|
||||
Variable::Scalar(name) => resolve_to_jvaluable(name, ctx),
|
||||
Variable::Stream(name) => {
|
||||
// return an empty stream for not found stream
|
||||
// here it ignores the join behaviour
|
||||
if ctx.data_cache.get(*name).is_none() {
|
||||
Ok(Box::new(()))
|
||||
} else {
|
||||
resolve_to_jvaluable(name, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_json_path<'i>(
|
||||
variable: &Variable<'_>,
|
||||
json_path: &str,
|
||||
should_flatten: bool,
|
||||
ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
let name = get_variable_name(variable);
|
||||
|
||||
let resolved = resolve_to_jvaluable(name, ctx)?;
|
||||
let (jvalue, tetraplets) = resolved.apply_json_path_with_tetraplets(json_path)?;
|
||||
|
||||
let jvalue = if should_flatten {
|
||||
if jvalue.len() > 1 {
|
||||
let jvalue = jvalue.into_iter().cloned().collect::<Vec<_>>();
|
||||
return crate::exec_err!(ExecutionError::FlatteningError(JValue::Array(jvalue)));
|
||||
}
|
||||
jvalue[0].clone()
|
||||
} else {
|
||||
let jvalue = jvalue.into_iter().cloned().collect::<Vec<_>>();
|
||||
JValue::Array(jvalue)
|
||||
};
|
||||
|
||||
Ok((jvalue, tetraplets))
|
||||
}
|
||||
|
||||
/// Constructs jvaluable result from `ExecutionCtx::data_cache` by name.
|
||||
pub(crate) fn resolve_to_jvaluable<'name, 'i, 'ctx>(
|
||||
name: &'name str,
|
||||
ctx: &'ctx ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<Box<dyn JValuable + 'ctx>> {
|
||||
use ExecutionError::VariableNotFound;
|
||||
|
||||
let value = ctx
|
||||
.data_cache
|
||||
.get(name)
|
||||
.ok_or_else(|| VariableNotFound(name.to_string()))?;
|
||||
|
||||
match value {
|
||||
AValue::JValueRef(value) => Ok(Box::new(value.clone())),
|
||||
AValue::JValueStreamRef(stream) => Ok(Box::new(stream.borrow())),
|
||||
AValue::JValueFoldCursor(fold_state) => {
|
||||
let peeked_value = fold_state.iterable.peek().unwrap();
|
||||
Ok(Box::new(peeked_value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use air_parser::ast::Variable;
|
||||
|
||||
pub(crate) fn get_variable_name<'a>(variable: &'a Variable<'_>) -> &'a str {
|
||||
match variable {
|
||||
Variable::Scalar(name) => name,
|
||||
Variable::Stream(name) => name,
|
||||
}
|
||||
}
|
92
air/src/execution_step/air/ap.rs
Normal file
92
air/src/execution_step/air/ap.rs
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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::boxed_value::Variable;
|
||||
use crate::execution_step::trace_handler::MergerApResult;
|
||||
use crate::execution_step::utils::apply_json_path;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
use apply_to_arguments::*;
|
||||
use utils::*;
|
||||
|
||||
use air_parser::ast::ApArgument;
|
||||
use air_parser::ast::AstVariable;
|
||||
use air_parser::ast::JsonPath;
|
||||
use air_parser::ast::{Ap, LastErrorPath};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for Ap<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
let should_touch_trace = should_touch_trace(self);
|
||||
|
||||
let merger_ap_result = if should_touch_trace {
|
||||
let merger_ap_result = trace_ctx.meet_ap_start()?;
|
||||
try_match_result_to_instr(&merger_ap_result, self)?;
|
||||
merger_ap_result
|
||||
} else {
|
||||
MergerApResult::Empty
|
||||
};
|
||||
|
||||
let result = apply_to_arg(&self.argument, exec_ctx, trace_ctx, should_touch_trace)?;
|
||||
save_result(&self.result, &merger_ap_result, result, exec_ctx)?;
|
||||
|
||||
if should_touch_trace {
|
||||
// if generations are empty, then this ap instruction operates only with scalars and data
|
||||
// shouldn't be updated
|
||||
let final_ap_result = to_ap_result(&merger_ap_result, self, exec_ctx);
|
||||
trace_ctx.meet_ap_end(final_ap_result);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn save_result<'ctx>(
|
||||
ap_result_type: &AstVariable<'ctx>,
|
||||
merger_ap_result: &MergerApResult,
|
||||
result: ResolvedCallResult,
|
||||
exec_ctx: &mut ExecutionCtx<'ctx>,
|
||||
) -> ExecutionResult<()> {
|
||||
match ap_result_type {
|
||||
AstVariable::Scalar(name) => set_scalar_result(result, name, exec_ctx),
|
||||
AstVariable::Stream(name) => {
|
||||
let generation = ap_result_to_generation(merger_ap_result);
|
||||
set_stream_result(result, generation, name.to_string(), exec_ctx).map(|_| ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_touch_trace(ap: &Ap<'_>) -> bool {
|
||||
match (&ap.argument, &ap.result) {
|
||||
(_, AstVariable::Stream(_)) => true,
|
||||
(ApArgument::JsonPath(json_path), _) => match &json_path.variable {
|
||||
AstVariable::Scalar(_) => false,
|
||||
AstVariable::Stream(_) => true,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
105
air/src/execution_step/air/ap/apply_to_arguments.rs
Normal file
105
air/src/execution_step/air/ap/apply_to_arguments.rs
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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::*;
|
||||
|
||||
pub(super) fn apply_to_arg(
|
||||
argument: &ApArgument<'_>,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
should_touch_trace: bool,
|
||||
) -> ExecutionResult<ResolvedCallResult> {
|
||||
let result = match argument {
|
||||
ApArgument::ScalarVariable(scalar_name) => apply_scalar(scalar_name, exec_ctx, trace_ctx, should_touch_trace)?,
|
||||
ApArgument::JsonPath(json_arg) => apply_json_argument(json_arg, exec_ctx, trace_ctx)?,
|
||||
ApArgument::LastError(error_path) => apply_last_error(error_path, exec_ctx, trace_ctx)?,
|
||||
ApArgument::Literal(value) => apply_const(value.to_string(), exec_ctx, trace_ctx),
|
||||
ApArgument::Number(value) => apply_const(value, exec_ctx, trace_ctx),
|
||||
ApArgument::Boolean(value) => apply_const(*value, exec_ctx, trace_ctx),
|
||||
ApArgument::EmptyArray => apply_const(serde_json::json!([]), exec_ctx, trace_ctx),
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn apply_scalar(
|
||||
scalar_name: &str,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
should_touch_trace: bool,
|
||||
) -> ExecutionResult<ResolvedCallResult> {
|
||||
use crate::execution_step::ExecutionError::VariableNotFound;
|
||||
use crate::execution_step::Scalar;
|
||||
|
||||
let scalar = exec_ctx
|
||||
.scalars
|
||||
.get(scalar_name)
|
||||
.ok_or_else(|| VariableNotFound(scalar_name.to_string()))?;
|
||||
|
||||
let mut result = match scalar {
|
||||
Scalar::JValueRef(result) => result.clone(),
|
||||
Scalar::JValueFoldCursor(iterator) => {
|
||||
let result = iterator.iterable.peek().expect(
|
||||
"peek always return elements inside fold,\
|
||||
this guaranteed by implementation of next and avoiding empty folds",
|
||||
);
|
||||
result.into_resolved_result()
|
||||
}
|
||||
};
|
||||
|
||||
if should_touch_trace {
|
||||
result.trace_pos = trace_ctx.trace_pos();
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn apply_const(value: impl Into<JValue>, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler) -> ResolvedCallResult {
|
||||
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())
|
||||
}
|
||||
|
||||
fn apply_last_error(
|
||||
error_path: &LastErrorPath,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
) -> ExecutionResult<ResolvedCallResult> {
|
||||
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());
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn apply_json_argument(
|
||||
json_arg: &JsonPath<'_>,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
) -> ExecutionResult<ResolvedCallResult> {
|
||||
let variable = Variable::from_ast(&json_arg.variable);
|
||||
let (jvalue, mut tetraplets) = apply_json_path(variable, json_arg.path, json_arg.should_flatten, 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());
|
||||
|
||||
Ok(result)
|
||||
}
|
89
air/src/execution_step/air/ap/utils.rs
Normal file
89
air/src/execution_step/air/ap/utils.rs
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 crate::execution_step::trace_handler::MergerApResult;
|
||||
use crate::execution_step::Generation;
|
||||
|
||||
use air_interpreter_data::ApResult;
|
||||
use air_parser::ast::Ap;
|
||||
use air_parser::ast::AstVariable;
|
||||
|
||||
pub(super) fn ap_result_to_generation(ap_result: &MergerApResult) -> Generation {
|
||||
match ap_result {
|
||||
MergerApResult::Empty => Generation::Last,
|
||||
MergerApResult::ApResult { res_generation, .. } => Generation::from_option(*res_generation),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn try_match_result_to_instr(merger_ap_result: &MergerApResult, instr: &Ap<'_>) -> ExecutionResult<()> {
|
||||
let res_generation = match merger_ap_result {
|
||||
MergerApResult::ApResult { res_generation } => *res_generation,
|
||||
MergerApResult::Empty => return Ok(()),
|
||||
};
|
||||
|
||||
match_position_variable(&instr.result, res_generation, merger_ap_result)
|
||||
}
|
||||
|
||||
fn match_position_variable(
|
||||
variable: &AstVariable<'_>,
|
||||
generation: Option<u32>,
|
||||
ap_result: &MergerApResult,
|
||||
) -> ExecutionResult<()> {
|
||||
use crate::execution_step::ExecutionError::ApResultNotCorrespondToInstr;
|
||||
|
||||
match (variable, generation) {
|
||||
(AstVariable::Stream(_), Some(_)) => Ok(()),
|
||||
(AstVariable::Scalar(_), None) => Ok(()),
|
||||
_ => return crate::exec_err!(ApResultNotCorrespondToInstr(ap_result.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn to_ap_result(merger_ap_result: &MergerApResult, instr: &Ap<'_>, exec_ctx: &ExecutionCtx<'_>) -> ApResult {
|
||||
if let MergerApResult::ApResult { res_generation } = merger_ap_result {
|
||||
let res_generation = option_to_vec(*res_generation);
|
||||
|
||||
return ApResult::new(res_generation);
|
||||
}
|
||||
|
||||
let res_generation = variable_to_generations(&instr.result, exec_ctx);
|
||||
ApResult::new(res_generation)
|
||||
}
|
||||
|
||||
fn option_to_vec(value: Option<u32>) -> Vec<u32> {
|
||||
match value {
|
||||
Some(value) => vec![value],
|
||||
None => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn variable_to_generations(variable: &AstVariable<'_>, exec_ctx: &ExecutionCtx<'_>) -> Vec<u32> {
|
||||
match variable {
|
||||
AstVariable::Scalar(_) => vec![],
|
||||
AstVariable::Stream(name) => {
|
||||
// unwrap here is safe because this function will be called only
|
||||
// when this stream's been created
|
||||
let stream = exec_ctx.streams.get(*name).unwrap();
|
||||
let generation = match stream.borrow().generations_count() {
|
||||
0 => 0,
|
||||
n => n - 1,
|
||||
};
|
||||
|
||||
vec![generation as u32]
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
pub(crate) mod call_result_setter;
|
||||
mod resolved_call;
|
||||
mod triplet;
|
||||
mod utils;
|
||||
@ -23,31 +24,30 @@ use resolved_call::ResolvedCall;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::ExecutionTraceCtx;
|
||||
use crate::contexts::execution::LastErrorDescriptor;
|
||||
use crate::execution::joinable::Joinable;
|
||||
use super::LastErrorDescriptor;
|
||||
use super::TraceHandler;
|
||||
use crate::execution_step::joinable::Joinable;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::joinable_call;
|
||||
use crate::log_instruction;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_parser::ast::Call;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for Call<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(call, exec_ctx, trace_ctx);
|
||||
exec_ctx.tracker.met_call();
|
||||
|
||||
let resolved_call = joinable_call!(ResolvedCall::new(self, exec_ctx), exec_ctx).map_err(|e| {
|
||||
set_last_error(self, exec_ctx, e.clone(), None);
|
||||
e
|
||||
})?;
|
||||
|
||||
let triplet = resolved_call.as_triplet();
|
||||
joinable_call!(resolved_call.execute(exec_ctx, trace_ctx, &self), exec_ctx).map_err(|e| {
|
||||
let tetraplet = SecurityTetraplet::from_triplet(triplet);
|
||||
let tetraplet = resolved_call.as_tetraplet();
|
||||
joinable_call!(resolved_call.execute(exec_ctx, trace_ctx), exec_ctx).map_err(|e| {
|
||||
set_last_error(self, exec_ctx, e.clone(), Some(tetraplet));
|
||||
|
||||
e
|
||||
})
|
||||
}
|
||||
@ -57,14 +57,14 @@ fn set_last_error<'i>(
|
||||
call: &Call<'i>,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
e: Rc<ExecutionError>,
|
||||
tetraplet: Option<SecurityTetraplet>,
|
||||
tetraplet: Option<RSecurityTetraplet>,
|
||||
) {
|
||||
let current_peer_id = match &tetraplet {
|
||||
// use tetraplet if they set, because an error could be propagated from data
|
||||
// (from CallServiceFailed state) and exec_ctx.current_peer_id won't mean
|
||||
// a peer where the error was occurred
|
||||
Some(tetraplet) => tetraplet.triplet.peer_pk.clone(),
|
||||
None => exec_ctx.current_peer_id.clone(),
|
||||
Some(tetraplet) => tetraplet.borrow().triplet.peer_pk.clone(),
|
||||
None => exec_ctx.current_peer_id.to_string(),
|
||||
};
|
||||
|
||||
log::warn!("call failed with an error `{}`, peerId `{}`", e, current_peer_id);
|
171
air/src/execution_step/air/call/call_result_setter.rs
Normal file
171
air/src/execution_step/air/call/call_result_setter.rs
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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::*;
|
||||
use crate::exec_err;
|
||||
use crate::execution_step::execution_context::*;
|
||||
use crate::execution_step::trace_handler::TraceHandler;
|
||||
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 air_interpreter_data::CallResult;
|
||||
use air_interpreter_data::Value;
|
||||
use air_parser::ast::CallOutputValue;
|
||||
|
||||
use std::cell::RefCell;
|
||||
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,
|
||||
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)?;
|
||||
Ok(CallResult::executed_scalar(result_value))
|
||||
}
|
||||
CallOutputValue::Variable(AstVariable::Stream(name)) => {
|
||||
let generation = set_stream_result(executed_result, Generation::Last, name.to_string(), exec_ctx)?;
|
||||
Ok(CallResult::executed_stream(result_value, generation))
|
||||
}
|
||||
CallOutputValue::None => Ok(CallResult::executed_scalar(result_value)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_result_from_value<'i>(
|
||||
value: Value,
|
||||
tetraplet: RSecurityTetraplet,
|
||||
trace_pos: usize,
|
||||
output: &CallOutputValue<'i>,
|
||||
exec_ctx: &mut ExecutionCtx<'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)?;
|
||||
}
|
||||
(CallOutputValue::Variable(AstVariable::Stream(name)), Value::Stream { value, generation }) => {
|
||||
let result = ResolvedCallResult::new(value, tetraplet, trace_pos);
|
||||
let generation = Generation::Nth(generation);
|
||||
let _ = set_stream_result(result, generation, name.to_string(), exec_ctx)?;
|
||||
}
|
||||
// it isn't needed to check there that output and value matches because
|
||||
// it's been already in trace handler
|
||||
_ => {}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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,
|
||||
generation: Generation,
|
||||
stream_name: String,
|
||||
exec_ctx: &mut ExecutionCtx<'_>,
|
||||
) -> ExecutionResult<u32> {
|
||||
let generation = match exec_ctx.streams.entry(stream_name) {
|
||||
Occupied(mut entry) => {
|
||||
// if result is an array, insert result to the end of the array
|
||||
entry.get_mut().borrow_mut().add_value(executed_result, generation)?
|
||||
}
|
||||
Vacant(entry) => {
|
||||
let stream = Stream::from_value(executed_result);
|
||||
entry.insert(RefCell::new(stream));
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
Ok(generation)
|
||||
}
|
||||
|
||||
/// Writes an executed state of a particle being sent to remote node.
|
||||
pub(crate) fn set_remote_call_result<'i>(
|
||||
peer_pk: String,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
trace_ctx: &mut TraceHandler,
|
||||
) {
|
||||
exec_ctx.next_peer_pks.push(peer_pk);
|
||||
exec_ctx.subtree_complete = false;
|
||||
|
||||
let new_call_result = CallResult::RequestSentBy(exec_ctx.current_peer_id.clone());
|
||||
trace_ctx.meet_call_end(new_call_result);
|
||||
}
|
@ -16,28 +16,28 @@
|
||||
|
||||
#![allow(unused_unsafe)] // for wasm_bindgen target where calling FFI is safe
|
||||
|
||||
use super::call_result_setter::*;
|
||||
use super::triplet::Triplet;
|
||||
use super::utils::*;
|
||||
use super::Call;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use crate::build_targets::CallServiceResult;
|
||||
use crate::build_targets::CALL_SERVICE_SUCCESS;
|
||||
use crate::contexts::execution_trace::*;
|
||||
use crate::log_targets::EXECUTED_STATE_CHANGING;
|
||||
use super::*;
|
||||
use crate::execution_step::air::ResolvedCallResult;
|
||||
use crate::execution_step::trace_handler::MergerCallResult;
|
||||
use crate::execution_step::trace_handler::TraceHandler;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
use crate::JValue;
|
||||
use crate::ResolvedTriplet;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_interpreter_data::CallResult;
|
||||
use air_parser::ast::{CallInstrArgValue, CallOutputValue};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Represents Call instruction with resolved internal parts.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(super) struct ResolvedCall<'i> {
|
||||
triplet: Rc<ResolvedTriplet>,
|
||||
tetraplet: RSecurityTetraplet,
|
||||
function_arg_paths: Rc<Vec<CallInstrArgValue<'i>>>,
|
||||
output: CallOutputValue<'i>,
|
||||
}
|
||||
@ -45,7 +45,7 @@ pub(super) struct ResolvedCall<'i> {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct ResolvedArguments {
|
||||
call_arguments: String,
|
||||
tetraplets: Vec<Vec<SecurityTetraplet>>,
|
||||
tetraplets: Vec<SecurityTetraplets>,
|
||||
}
|
||||
|
||||
impl<'i> ResolvedCall<'i> {
|
||||
@ -53,35 +53,27 @@ impl<'i> ResolvedCall<'i> {
|
||||
pub(super) fn new(raw_call: &Call<'i>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<Self> {
|
||||
let triplet = Triplet::try_from(&raw_call.peer_part, &raw_call.function_part)?;
|
||||
let triplet = triplet.resolve(exec_ctx)?;
|
||||
let triplet = Rc::new(triplet);
|
||||
let tetraplet = SecurityTetraplet::from_triplet(triplet);
|
||||
let tetraplet = Rc::new(RefCell::new(tetraplet));
|
||||
|
||||
Ok(Self {
|
||||
triplet,
|
||||
tetraplet,
|
||||
function_arg_paths: raw_call.args.clone(),
|
||||
output: raw_call.output.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Executes resolved instruction, updates contexts based on a execution result.
|
||||
pub(super) fn execute(
|
||||
self,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
trace_ctx: &mut ExecutionTraceCtx,
|
||||
instruction: &Call<'i>,
|
||||
) -> ExecutionResult<()> {
|
||||
use CallResult::Executed;
|
||||
use ExecutedState::Call;
|
||||
use ExecutionError::CallServiceResultDeError as DeError;
|
||||
|
||||
let should_execute = self.prepare_executed_state(exec_ctx, trace_ctx, instruction)?;
|
||||
/// Executes resolved instruction, updates contexts based on a execution_step result.
|
||||
pub(super) fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
let should_execute = self.prepare_current_executed_state(exec_ctx, trace_ctx)?;
|
||||
if !should_execute {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// call can be executed only on peers with such peer_id
|
||||
if self.triplet.peer_pk != exec_ctx.current_peer_id {
|
||||
set_remote_call_result(self.triplet.peer_pk.clone(), exec_ctx, trace_ctx);
|
||||
|
||||
let triplet = &self.tetraplet.borrow().triplet;
|
||||
if triplet.peer_pk.as_str() != exec_ctx.current_peer_id.as_str() {
|
||||
set_remote_call_result(triplet.peer_pk.clone(), exec_ctx, trace_ctx);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -94,12 +86,24 @@ impl<'i> ResolvedCall<'i> {
|
||||
|
||||
let service_result = unsafe {
|
||||
crate::build_targets::call_service(
|
||||
&self.triplet.service_id,
|
||||
&self.triplet.function_name,
|
||||
&triplet.service_id,
|
||||
&triplet.function_name,
|
||||
&call_arguments,
|
||||
&serialized_tetraplets,
|
||||
)
|
||||
};
|
||||
exec_ctx.tracker.met_executed_call();
|
||||
|
||||
self.update_state_with_service_result(service_result, exec_ctx, trace_ctx)
|
||||
}
|
||||
|
||||
fn update_state_with_service_result(
|
||||
&self,
|
||||
service_result: CallServiceResult,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
trace_ctx: &mut TraceHandler,
|
||||
) -> ExecutionResult<()> {
|
||||
use ExecutionError::CallServiceResultDeError as DeError;
|
||||
|
||||
// check that service call succeeded
|
||||
let service_result = handle_service_error(service_result, trace_ctx)?;
|
||||
@ -107,63 +111,43 @@ impl<'i> ResolvedCall<'i> {
|
||||
let result: JValue = serde_json::from_str(&service_result.result).map_err(|e| DeError(service_result, e))?;
|
||||
let result = Rc::new(result);
|
||||
|
||||
set_local_call_result(result.clone(), self.triplet.clone(), &self.output, exec_ctx)?;
|
||||
let new_executed_state = Call(Executed(result));
|
||||
let trace_pos = trace_ctx.trace_pos();
|
||||
|
||||
log::trace!(
|
||||
target: EXECUTED_STATE_CHANGING,
|
||||
" adding new call executed state {:?}",
|
||||
new_executed_state
|
||||
);
|
||||
|
||||
trace_ctx.new_trace.push_back(new_executed_state);
|
||||
let executed_result = ResolvedCallResult::new(result, self.tetraplet.clone(), trace_pos);
|
||||
let new_call_result = set_local_result(executed_result, &self.output, exec_ctx)?;
|
||||
trace_ctx.meet_call_end(new_call_result);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn as_triplet(&self) -> Rc<ResolvedTriplet> {
|
||||
self.triplet.clone()
|
||||
pub(super) fn as_tetraplet(&self) -> RSecurityTetraplet {
|
||||
self.tetraplet.clone()
|
||||
}
|
||||
|
||||
/// Determine whether this call should be really called and adjust prev executed trace accordingly.
|
||||
fn prepare_executed_state(
|
||||
fn prepare_current_executed_state(
|
||||
&self,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
trace_ctx: &mut ExecutionTraceCtx,
|
||||
instruction: &Call<'i>,
|
||||
trace_ctx: &mut TraceHandler,
|
||||
) -> ExecutionResult<bool> {
|
||||
if trace_ctx.current_subtree_size == 0 {
|
||||
log::trace!(
|
||||
target: EXECUTED_STATE_CHANGING,
|
||||
" previous executed trace state wasn't found"
|
||||
);
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
trace_ctx.current_subtree_size -= 1;
|
||||
// unwrap is safe here, because current_subtree_size depends on current_path len,
|
||||
// and it's been checked previously
|
||||
let prev_state = trace_ctx.current_trace.pop_front().unwrap();
|
||||
|
||||
log::trace!(
|
||||
target: EXECUTED_STATE_CHANGING,
|
||||
" previous executed trace found {:?}",
|
||||
prev_state
|
||||
);
|
||||
let (call_result, trace_pos) = match trace_ctx.meet_call_start(&self.output)? {
|
||||
MergerCallResult::CallResult { value, trace_pos } => (value, trace_pos),
|
||||
MergerCallResult::Empty => return Ok(true),
|
||||
};
|
||||
|
||||
handle_prev_state(
|
||||
&self.triplet,
|
||||
&self.tetraplet,
|
||||
&self.output,
|
||||
prev_state,
|
||||
call_result,
|
||||
trace_pos,
|
||||
exec_ctx,
|
||||
trace_ctx,
|
||||
instruction,
|
||||
)
|
||||
}
|
||||
|
||||
/// Prepare arguments of this call instruction by resolving and preparing their security tetraplets.
|
||||
fn resolve_args(&self, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<ResolvedArguments> {
|
||||
use crate::execution::utils::resolve_to_args;
|
||||
use crate::execution_step::utils::resolve_to_args;
|
||||
|
||||
let function_args = self.function_arg_paths.iter();
|
||||
let mut call_arguments = Vec::new();
|
||||
@ -186,12 +170,14 @@ impl<'i> ResolvedCall<'i> {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::build_targets::CallServiceResult;
|
||||
|
||||
fn handle_service_error(
|
||||
service_result: CallServiceResult,
|
||||
trace_ctx: &mut ExecutionTraceCtx,
|
||||
trace_ctx: &mut TraceHandler,
|
||||
) -> ExecutionResult<CallServiceResult> {
|
||||
use crate::build_targets::CALL_SERVICE_SUCCESS;
|
||||
use CallResult::CallServiceFailed;
|
||||
use ExecutedState::Call;
|
||||
|
||||
if service_result.ret_code == CALL_SERVICE_SUCCESS {
|
||||
return Ok(service_result);
|
||||
@ -201,9 +187,7 @@ fn handle_service_error(
|
||||
let error = ExecutionError::LocalServiceError(service_result.ret_code, error_message.clone());
|
||||
let error = Rc::new(error);
|
||||
|
||||
trace_ctx
|
||||
.new_trace
|
||||
.push_back(Call(CallServiceFailed(service_result.ret_code, error_message)));
|
||||
trace_ctx.meet_call_end(CallServiceFailed(service_result.ret_code, error_message));
|
||||
|
||||
Err(error)
|
||||
}
|
@ -45,9 +45,7 @@ impl<'a, 'i> Triplet<'a, 'i> {
|
||||
Ok((peer_pk, peer_service_id, func_name))
|
||||
}
|
||||
(PeerPk(peer_pk), ServiceIdWithFuncName(service_id, func_name)) => Ok((peer_pk, service_id, func_name)),
|
||||
(PeerPk(_), FuncName(_)) => exec_err!(ExecutionError::InstructionError(String::from(
|
||||
"call should have service id specified by peer part or function part",
|
||||
))),
|
||||
(PeerPk(_), FuncName(_)) => exec_err!(ExecutionError::IncorrectCallTriplet),
|
||||
}?;
|
||||
|
||||
Ok(Self {
|
||||
@ -79,31 +77,23 @@ impl<'a, 'i> Triplet<'a, 'i> {
|
||||
/// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc.
|
||||
// TODO: return Rc<String> to avoid excess cloning
|
||||
fn resolve_to_string<'i>(value: &CallInstrValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<String> {
|
||||
use crate::execution::utils::get_variable_name;
|
||||
use crate::execution::utils::resolve_to_jvaluable;
|
||||
use crate::execution_step::utils::resolve_ast_variable;
|
||||
|
||||
let resolved = match value {
|
||||
CallInstrValue::InitPeerId => ctx.init_peer_id.clone(),
|
||||
CallInstrValue::Literal(value) => value.to_string(),
|
||||
CallInstrValue::Variable(variable) => {
|
||||
let name = get_variable_name(variable);
|
||||
let resolved = resolve_to_jvaluable(name, ctx)?;
|
||||
let resolved = resolve_ast_variable(variable, ctx)?;
|
||||
let jvalue = resolved.into_jvalue();
|
||||
jvalue_to_string(jvalue)?
|
||||
}
|
||||
CallInstrValue::JsonPath {
|
||||
variable,
|
||||
path,
|
||||
should_flatten,
|
||||
} => {
|
||||
CallInstrValue::JsonPath(json_path) => {
|
||||
// this is checked on the parsing stage
|
||||
debug_assert!(*should_flatten);
|
||||
debug_assert!(json_path.should_flatten);
|
||||
|
||||
let name = get_variable_name(variable);
|
||||
|
||||
let resolved = resolve_to_jvaluable(name, ctx)?;
|
||||
let resolved = resolved.apply_json_path(path)?;
|
||||
vec_to_string(resolved, path)?
|
||||
let resolved = resolve_ast_variable(&json_path.variable, ctx)?;
|
||||
let resolved = resolved.apply_json_path(json_path.path)?;
|
||||
vec_to_string(resolved, json_path.path)?
|
||||
}
|
||||
};
|
||||
|
67
air/src/execution_step/air/call/utils.rs
Normal file
67
air/src/execution_step/air/call/utils.rs
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2020 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::*;
|
||||
use crate::exec_err;
|
||||
use crate::execution_step::trace_handler::TraceHandler;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
|
||||
use crate::execution_step::air::call::call_result_setter::set_result_from_value;
|
||||
use air_interpreter_data::CallResult;
|
||||
use air_parser::ast::CallOutputValue;
|
||||
|
||||
/// This function looks at the existing call state, validates it,
|
||||
/// and returns Ok(true) if the call should be executed further.
|
||||
pub(super) fn handle_prev_state<'i>(
|
||||
tetraplet: &RSecurityTetraplet,
|
||||
output: &CallOutputValue<'i>,
|
||||
prev_result: CallResult,
|
||||
trace_pos: usize,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
trace_ctx: &mut TraceHandler,
|
||||
) -> ExecutionResult<bool> {
|
||||
use CallResult::*;
|
||||
|
||||
let result = match &prev_result {
|
||||
// this call was failed on one of the previous executions,
|
||||
// here it's needed to bubble this special error up
|
||||
CallServiceFailed(ret_code, err_msg) => {
|
||||
exec_ctx.subtree_complete = false;
|
||||
exec_err!(ExecutionError::LocalServiceError(*ret_code, err_msg.clone()))
|
||||
}
|
||||
RequestSentBy(..) => {
|
||||
// check whether current node can execute this call
|
||||
let is_current_peer = tetraplet.borrow().triplet.peer_pk.as_str() == exec_ctx.current_peer_id.as_str();
|
||||
if is_current_peer {
|
||||
// if this peer could execute this call early return and
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
exec_ctx.subtree_complete = false;
|
||||
Ok(false)
|
||||
}
|
||||
// this instruction's been already executed
|
||||
Executed(value) => {
|
||||
set_result_from_value(value.clone(), tetraplet.clone(), trace_pos, output, exec_ctx)?;
|
||||
|
||||
exec_ctx.subtree_complete = true;
|
||||
Ok(false)
|
||||
}
|
||||
};
|
||||
|
||||
trace_ctx.meet_call_end(prev_result);
|
||||
result
|
||||
}
|
@ -14,10 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::contexts::execution::ExecutionCtx;
|
||||
use crate::execution::air::ExecutionResult;
|
||||
use crate::execution::utils::get_variable_name;
|
||||
use crate::execution::utils::resolve_to_jvaluable;
|
||||
use crate::execution_step::air::ExecutionResult;
|
||||
use crate::execution_step::execution_context::ExecutionCtx;
|
||||
use crate::execution_step::utils::resolve_ast_variable;
|
||||
use crate::JValue;
|
||||
|
||||
use air_parser::ast;
|
||||
@ -54,40 +53,25 @@ pub(crate) fn are_matchable_eq<'ctx>(
|
||||
(matchable, Number(value)) => compare_matchable(matchable, exec_ctx, make_number_comparator(value)),
|
||||
|
||||
(Variable(left_variable), Variable(right_variable)) => {
|
||||
let left_name = get_variable_name(left_variable);
|
||||
let left_jvaluable = resolve_to_jvaluable(left_name, exec_ctx)?;
|
||||
let left_jvaluable = resolve_ast_variable(left_variable, exec_ctx)?;
|
||||
let left_value = left_jvaluable.as_jvalue();
|
||||
|
||||
let right_name = get_variable_name(right_variable);
|
||||
let right_jvaluable = resolve_to_jvaluable(right_name, exec_ctx)?;
|
||||
let right_jvaluable = resolve_ast_variable(right_variable, exec_ctx)?;
|
||||
let right_value = right_jvaluable.as_jvalue();
|
||||
|
||||
Ok(left_value == right_value)
|
||||
}
|
||||
(
|
||||
JsonPath {
|
||||
variable: lv,
|
||||
path: lp,
|
||||
should_flatten: lsf,
|
||||
},
|
||||
JsonPath {
|
||||
variable: rv,
|
||||
path: rp,
|
||||
should_flatten: rsf,
|
||||
},
|
||||
) => {
|
||||
(JsonPath(lhs), JsonPath(rhs)) => {
|
||||
// TODO: improve comparison
|
||||
if lsf != rsf {
|
||||
if lhs.should_flatten != rhs.should_flatten {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let left_name = get_variable_name(lv);
|
||||
let left_jvaluable = resolve_to_jvaluable(left_name, exec_ctx)?;
|
||||
let left_value = left_jvaluable.apply_json_path(lp)?;
|
||||
let left_jvaluable = resolve_ast_variable(&lhs.variable, exec_ctx)?;
|
||||
let left_value = left_jvaluable.apply_json_path(lhs.path)?;
|
||||
|
||||
let right_name = get_variable_name(rv);
|
||||
let right_jvaluable = resolve_to_jvaluable(right_name, exec_ctx)?;
|
||||
let right_value = right_jvaluable.apply_json_path(rp)?;
|
||||
let right_jvaluable = resolve_ast_variable(&rhs.variable, exec_ctx)?;
|
||||
let right_value = right_jvaluable.apply_json_path(rhs.path)?;
|
||||
|
||||
Ok(left_value == right_value)
|
||||
}
|
||||
@ -123,22 +107,20 @@ fn compare_matchable<'ctx>(
|
||||
let jvalue = (*bool).into();
|
||||
Ok(comparator(Cow::Owned(jvalue)))
|
||||
}
|
||||
EmptyArray => {
|
||||
let jvalue = JValue::Array(vec![]);
|
||||
Ok(comparator(Cow::Owned(jvalue)))
|
||||
}
|
||||
Variable(variable) => {
|
||||
let name = get_variable_name(variable);
|
||||
let jvaluable = resolve_to_jvaluable(name, exec_ctx)?;
|
||||
let jvaluable = resolve_ast_variable(variable, exec_ctx)?;
|
||||
let jvalue = jvaluable.as_jvalue();
|
||||
Ok(comparator(jvalue))
|
||||
}
|
||||
JsonPath {
|
||||
variable,
|
||||
path,
|
||||
should_flatten,
|
||||
} => {
|
||||
let var_name = get_variable_name(variable);
|
||||
let jvaluable = resolve_to_jvaluable(var_name, exec_ctx)?;
|
||||
let jvalues = jvaluable.apply_json_path(path)?;
|
||||
JsonPath(json_path) => {
|
||||
let jvaluable = resolve_ast_variable(&json_path.variable, exec_ctx)?;
|
||||
let jvalues = jvaluable.apply_json_path(json_path.path)?;
|
||||
|
||||
let jvalue = if *should_flatten {
|
||||
let jvalue = if json_path.should_flatten {
|
||||
if jvalues.len() != 1 {
|
||||
return Ok(false);
|
||||
}
|
@ -14,6 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod compare_matchable;
|
||||
mod comparator;
|
||||
|
||||
pub(super) use compare_matchable::are_matchable_eq;
|
||||
pub(super) use comparator::are_matchable_eq;
|
51
air/src/execution_step/air/fold/fold_state.rs
Normal file
51
air/src/execution_step/air/fold/fold_state.rs
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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::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)]
|
||||
pub(crate) enum IterableType {
|
||||
Scalar,
|
||||
Stream(usize),
|
||||
}
|
||||
|
||||
impl<'i> FoldState<'i> {
|
||||
pub(crate) fn from_iterable(
|
||||
iterable: IterableValue,
|
||||
iterable_type: IterableType,
|
||||
instr_head: Rc<Instruction<'i>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
iterable,
|
||||
iterable_type,
|
||||
instr_head,
|
||||
met_variables: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
32
air/src/execution_step/air/fold/mod.rs
Normal file
32
air/src/execution_step/air/fold/mod.rs
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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 crate::execution_step::boxed_value::*;
|
210
air/src/execution_step/air/fold/utils.rs
Normal file
210
air/src/execution_step/air/fold/utils.rs
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2020 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::*;
|
||||
use crate::exec_err;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::JValue;
|
||||
|
||||
use air_parser::ast;
|
||||
use jsonpath_lib::select;
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
// TODO: refactor this file after switching to boxed value
|
||||
|
||||
pub(crate) type IterableValue = Box<dyn for<'ctx> Iterable<'ctx, Item = IterableItem<'ctx>>>;
|
||||
|
||||
pub(crate) enum FoldIterableScalar {
|
||||
Empty,
|
||||
Scalar(IterableValue),
|
||||
}
|
||||
|
||||
pub(crate) enum FoldIterableStream {
|
||||
Empty,
|
||||
Stream(Vec<IterableValue>),
|
||||
}
|
||||
|
||||
/// Constructs iterable value for given scalar iterable.
|
||||
pub(crate) fn construct_scalar_iterable_value<'ctx>(
|
||||
ast_iterable: &ast::IterableScalarValue<'ctx>,
|
||||
exec_ctx: &ExecutionCtx<'ctx>,
|
||||
) -> ExecutionResult<FoldIterableScalar> {
|
||||
match ast_iterable {
|
||||
ast::IterableScalarValue::ScalarVariable(scalar_name) => create_scalar_iterable(exec_ctx, scalar_name),
|
||||
ast::IterableScalarValue::JsonPath {
|
||||
scalar_name,
|
||||
path,
|
||||
should_flatten,
|
||||
} => create_scalar_json_path_iterable(exec_ctx, scalar_name, path, *should_flatten),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs iterable value for given stream iterable.
|
||||
pub(crate) fn construct_stream_iterable_value<'ctx>(
|
||||
stream_name: &'ctx str,
|
||||
exec_ctx: &ExecutionCtx<'ctx>,
|
||||
) -> ExecutionResult<FoldIterableStream> {
|
||||
match exec_ctx.streams.get(stream_name) {
|
||||
Some(stream) => {
|
||||
let stream = stream.borrow();
|
||||
if stream.is_empty() {
|
||||
return Ok(FoldIterableStream::Empty);
|
||||
}
|
||||
|
||||
let mut iterables = Vec::with_capacity(stream.generations_count());
|
||||
|
||||
for iterable in stream.slice_iter(Generation::Last).unwrap() {
|
||||
if iterable.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let call_results = iterable.to_vec();
|
||||
let foldable = IterableVecResolvedCall::init(call_results);
|
||||
let foldable: IterableValue = Box::new(foldable);
|
||||
iterables.push(foldable);
|
||||
}
|
||||
|
||||
Ok(FoldIterableStream::Stream(iterables))
|
||||
}
|
||||
// it's possible to met streams without variables at the moment in fold,
|
||||
// they should be treated as empty.
|
||||
None => Ok(FoldIterableStream::Empty),
|
||||
}
|
||||
}
|
||||
|
||||
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)) => {
|
||||
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> {
|
||||
use ExecutionError::IncompatibleJValueType;
|
||||
|
||||
let len = match &call_result.result.deref() {
|
||||
JValue::Array(array) => {
|
||||
if array.is_empty() {
|
||||
// skip fold if array is empty
|
||||
return Ok(FoldIterableScalar::Empty);
|
||||
}
|
||||
array.len()
|
||||
}
|
||||
v => return exec_err!(IncompatibleJValueType((*v).clone(), "array")),
|
||||
};
|
||||
|
||||
let foldable = IterableResolvedCall::init(call_result, len);
|
||||
let foldable = Box::new(foldable);
|
||||
let iterable = FoldIterableScalar::Scalar(foldable);
|
||||
|
||||
Ok(iterable)
|
||||
}
|
||||
|
||||
fn create_scalar_json_path_iterable<'ctx>(
|
||||
exec_ctx: &ExecutionCtx<'ctx>,
|
||||
scalar_name: &str,
|
||||
json_path: &str,
|
||||
should_flatten: bool,
|
||||
) -> ExecutionResult<FoldIterableScalar> {
|
||||
match exec_ctx.scalars.get(scalar_name) {
|
||||
Some(Scalar::JValueRef(variable)) => {
|
||||
let jvalues = apply_json_path(&variable.result, json_path)?;
|
||||
from_jvalues(jvalues, variable.tetraplet.clone(), json_path, should_flatten)
|
||||
}
|
||||
Some(Scalar::JValueFoldCursor(fold_state)) => {
|
||||
let iterable_value = fold_state.iterable.peek().unwrap();
|
||||
let jvalues = iterable_value.apply_json_path(json_path)?;
|
||||
let tetraplet = as_tetraplet(&iterable_value);
|
||||
|
||||
from_jvalues(jvalues, tetraplet, json_path, should_flatten)
|
||||
}
|
||||
_ => return exec_err!(ExecutionError::VariableNotFound(scalar_name.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_json_path<'jvalue, 'str>(
|
||||
jvalue: &'jvalue JValue,
|
||||
json_path: &'str str,
|
||||
) -> ExecutionResult<Vec<&'jvalue JValue>> {
|
||||
use ExecutionError::JValueJsonPathError;
|
||||
|
||||
select(jvalue, json_path).map_err(|e| Rc::new(JValueJsonPathError(jvalue.clone(), json_path.to_string(), e)))
|
||||
}
|
||||
|
||||
/// Applies json_path to provided jvalues and construct IterableValue from the result and given triplet.
|
||||
fn from_jvalues(
|
||||
jvalues: Vec<&JValue>,
|
||||
tetraplet: RSecurityTetraplet,
|
||||
json_path: &str,
|
||||
should_flatten: bool,
|
||||
) -> ExecutionResult<FoldIterableScalar> {
|
||||
let jvalues = construct_iterable_jvalues(jvalues, should_flatten)?;
|
||||
|
||||
if jvalues.is_empty() {
|
||||
return Ok(FoldIterableScalar::Empty);
|
||||
}
|
||||
|
||||
tetraplet.borrow_mut().add_json_path(json_path);
|
||||
|
||||
let foldable = IterableJsonPathResult::init(jvalues, tetraplet);
|
||||
let iterable = FoldIterableScalar::Scalar(Box::new(foldable));
|
||||
Ok(iterable)
|
||||
}
|
||||
|
||||
fn construct_iterable_jvalues(jvalues: Vec<&JValue>, should_flatten: bool) -> ExecutionResult<Vec<JValue>> {
|
||||
if !should_flatten {
|
||||
let jvalues = jvalues.into_iter().cloned().collect();
|
||||
return Ok(jvalues);
|
||||
}
|
||||
|
||||
if jvalues.len() != 1 {
|
||||
let jvalues = jvalues.into_iter().cloned().collect();
|
||||
let jvalue = JValue::Array(jvalues);
|
||||
return exec_err!(ExecutionError::FlatteningError(jvalue));
|
||||
}
|
||||
|
||||
match jvalues[0] {
|
||||
JValue::Array(values) => Ok(values.clone()),
|
||||
_ => {
|
||||
let jvalues = jvalues.into_iter().cloned().collect();
|
||||
let jvalue = JValue::Array(jvalues);
|
||||
exec_err!(ExecutionError::FlatteningError(jvalue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_tetraplet(iterable: &IterableItem<'_>) -> RSecurityTetraplet {
|
||||
use IterableItem::*;
|
||||
|
||||
let tetraplet = match iterable {
|
||||
RefRef((_, tetraplet, _)) => tetraplet,
|
||||
RefValue((_, tetraplet, _)) => tetraplet,
|
||||
RcValue((_, tetraplet, _)) => tetraplet,
|
||||
};
|
||||
|
||||
(*tetraplet).clone()
|
||||
}
|
89
air/src/execution_step/air/fold/variable_handler.rs
Normal file
89
air/src/execution_step/air/fold/variable_handler.rs
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
62
air/src/execution_step/air/fold_scalar.rs
Normal file
62
air/src/execution_step/air/fold_scalar.rs
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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::fold::*;
|
||||
use super::ExecutableInstruction;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionResult;
|
||||
use super::TraceHandler;
|
||||
use crate::log_instruction;
|
||||
|
||||
use air_parser::ast::FoldScalar;
|
||||
use air_parser::ast::Instruction;
|
||||
use std::rc::Rc;
|
||||
|
||||
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)? {
|
||||
FoldIterableScalar::Empty => Ok(()),
|
||||
FoldIterableScalar::Scalar(iterable) => fold(
|
||||
iterable,
|
||||
IterableType::Scalar,
|
||||
self.iterator,
|
||||
self.instruction.clone(),
|
||||
exec_ctx,
|
||||
trace_ctx,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn fold<'i>(
|
||||
iterable: IterableValue,
|
||||
iterable_type: IterableType,
|
||||
iterator: &'i str,
|
||||
instruction: Rc<Instruction<'i>>,
|
||||
exec_ctx: &mut ExecutionCtx<'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)?;
|
||||
|
||||
instruction.execute(exec_ctx, trace_ctx)?;
|
||||
|
||||
variable_handler.cleanup(exec_ctx);
|
||||
|
||||
Ok(())
|
||||
}
|
68
air/src/execution_step/air/fold_stream.rs
Normal file
68
air/src/execution_step/air/fold_stream.rs
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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::fold::*;
|
||||
use super::fold_scalar::fold;
|
||||
use super::ExecutableInstruction;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionResult;
|
||||
use super::TraceHandler;
|
||||
use crate::log_instruction;
|
||||
|
||||
use air_parser::ast::FoldStream;
|
||||
|
||||
impl<'i> ExecutableInstruction<'i> for FoldStream<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(fold, exec_ctx, trace_ctx);
|
||||
|
||||
let iterables = match construct_stream_iterable_value(self.stream_name, exec_ctx)? {
|
||||
FoldIterableStream::Empty => return Ok(()),
|
||||
FoldIterableStream::Stream(iterables) => iterables,
|
||||
};
|
||||
|
||||
let fold_id = exec_ctx.tracker.fold.seen_stream_count;
|
||||
trace_ctx.meet_fold_start(fold_id)?;
|
||||
|
||||
for iterable in iterables {
|
||||
let value = match iterable.peek() {
|
||||
Some(value) => value,
|
||||
// it's ok, because some generation level of a stream on some point inside execution
|
||||
// flow could contain zero values
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let value_pos = value.pos();
|
||||
trace_ctx.meet_iteration_start(fold_id, value_pos)?;
|
||||
fold(
|
||||
iterable,
|
||||
IterableType::Stream(fold_id),
|
||||
self.iterator,
|
||||
self.instruction.clone(),
|
||||
exec_ctx,
|
||||
trace_ctx,
|
||||
)?;
|
||||
trace_ctx.meet_generation_end(fold_id)?;
|
||||
|
||||
if !exec_ctx.subtree_complete {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
trace_ctx.meet_fold_end(fold_id)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -18,15 +18,15 @@ use super::compare_matchable::are_matchable_eq;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::ExecutionTraceCtx;
|
||||
use crate::execution::joinable::Joinable;
|
||||
use super::TraceHandler;
|
||||
use crate::execution_step::joinable::Joinable;
|
||||
use crate::joinable;
|
||||
use crate::log_instruction;
|
||||
|
||||
use air_parser::ast::Match;
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for Match<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(match_, exec_ctx, trace_ctx);
|
||||
|
||||
let are_values_equal = joinable!(
|
@ -18,15 +18,15 @@ use super::compare_matchable::are_matchable_eq;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::ExecutionTraceCtx;
|
||||
use crate::execution::joinable::Joinable;
|
||||
use super::TraceHandler;
|
||||
use crate::execution_step::joinable::Joinable;
|
||||
use crate::joinable;
|
||||
use crate::log_instruction;
|
||||
|
||||
use air_parser::ast::MisMatch;
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for MisMatch<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(match_, exec_ctx, trace_ctx);
|
||||
|
||||
let are_values_equal = joinable!(
|
@ -14,11 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod ap;
|
||||
mod call;
|
||||
mod compare_matchable;
|
||||
mod fold;
|
||||
mod fold_scalar;
|
||||
mod fold_stream;
|
||||
mod match_;
|
||||
mod mismatch;
|
||||
mod next;
|
||||
mod null;
|
||||
mod par;
|
||||
mod seq;
|
||||
@ -26,11 +30,14 @@ mod xor;
|
||||
|
||||
pub(crate) use fold::FoldState;
|
||||
|
||||
pub(self) use super::ExecutionError;
|
||||
pub(self) use super::ExecutionResult;
|
||||
pub(self) use crate::contexts::execution::ExecutionCtx;
|
||||
pub(self) use crate::contexts::execution::LastErrorDescriptor;
|
||||
pub(self) use crate::contexts::execution_trace::ExecutionTraceCtx;
|
||||
use super::boxed_value::ResolvedCallResult;
|
||||
use super::boxed_value::Scalar;
|
||||
use super::execution_context::*;
|
||||
use super::Catchable;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use crate::execution_step::TraceHandler;
|
||||
|
||||
use air_parser::ast::Instruction;
|
||||
|
||||
@ -45,7 +52,7 @@ macro_rules! execute {
|
||||
|
||||
let instruction = format!("{}", $self);
|
||||
let last_error =
|
||||
LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.clone(), None);
|
||||
LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.to_string(), None);
|
||||
$exec_ctx.last_error = Some(last_error);
|
||||
Err(e)
|
||||
}
|
||||
@ -54,6 +61,31 @@ macro_rules! execute {
|
||||
};
|
||||
}
|
||||
|
||||
/// Executes fold instruction, updates last error if needed, and call error_exit of TraceHandler.
|
||||
macro_rules! execute_fold {
|
||||
($self:expr, $instr:expr, $exec_ctx:ident, $trace_ctx:ident) => {{
|
||||
$exec_ctx.tracker.met_fold_stream();
|
||||
let fold_id = $exec_ctx.tracker.fold.seen_stream_count;
|
||||
|
||||
match $instr.execute($exec_ctx, $trace_ctx) {
|
||||
Err(e) => {
|
||||
$trace_ctx.fold_end_with_error(fold_id);
|
||||
|
||||
if !$exec_ctx.last_error_could_be_set {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
let instruction = format!("{}", $self);
|
||||
let last_error =
|
||||
LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.to_string(), None);
|
||||
$exec_ctx.last_error = Some(last_error);
|
||||
Err(e)
|
||||
}
|
||||
v => v,
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Executes match/mismatch instructions and updates last error if error type wasn't
|
||||
/// MatchWithoutXorError or MismatchWithoutXorError.
|
||||
macro_rules! execute_match_mismatch {
|
||||
@ -71,7 +103,7 @@ macro_rules! execute_match_mismatch {
|
||||
|
||||
let instruction = format!("{}", $self);
|
||||
let last_error =
|
||||
LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.clone(), None);
|
||||
LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.to_string(), None);
|
||||
$exec_ctx.last_error = Some(last_error);
|
||||
Err(e)
|
||||
}
|
||||
@ -81,17 +113,19 @@ macro_rules! execute_match_mismatch {
|
||||
}
|
||||
|
||||
pub(crate) trait ExecutableInstruction<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()>;
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()>;
|
||||
}
|
||||
|
||||
impl<'i> ExecutableInstruction<'i> for Instruction<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
match self {
|
||||
// call isn't wrapped by the execute macro because
|
||||
// it internally sets last_error with resolved triplet
|
||||
Instruction::Call(call) => call.execute(exec_ctx, trace_ctx),
|
||||
|
||||
Instruction::Fold(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
||||
Instruction::Ap(ap) => execute!(self, ap, exec_ctx, trace_ctx),
|
||||
Instruction::FoldScalar(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
||||
Instruction::FoldStream(fold) => execute_fold!(self, fold, exec_ctx, trace_ctx),
|
||||
Instruction::Next(next) => execute!(self, next, exec_ctx, trace_ctx),
|
||||
Instruction::Null(null) => execute!(self, null, exec_ctx, trace_ctx),
|
||||
Instruction::Par(par) => execute!(self, par, exec_ctx, trace_ctx),
|
||||
@ -112,15 +146,23 @@ macro_rules! log_instruction {
|
||||
($instr_name:expr, $exec_ctx:expr, $trace_ctx:expr) => {
|
||||
log::debug!(target: crate::log_targets::INSTRUCTION, "> {}", stringify!($instr_name));
|
||||
|
||||
let mut data_cache_log = String::from(" data cache:");
|
||||
if $exec_ctx.data_cache.is_empty() {
|
||||
data_cache_log.push_str(" empty");
|
||||
let mut variables = String::from(" scalars:");
|
||||
if $exec_ctx.scalars.is_empty() {
|
||||
variables.push_str(" empty");
|
||||
}
|
||||
for (key, value) in $exec_ctx.data_cache.iter() {
|
||||
data_cache_log.push_str(&format!("\n {} => {}", key, value));
|
||||
for (key, value) in $exec_ctx.scalars.iter() {
|
||||
variables.push_str(&format!("\n {} => {}", key, value));
|
||||
}
|
||||
|
||||
log::trace!(target: crate::log_targets::DATA_CACHE, "{}", data_cache_log);
|
||||
variables.push_str(" streams:");
|
||||
if $exec_ctx.streams.is_empty() {
|
||||
variables.push_str(" empty");
|
||||
}
|
||||
for (key, value) in $exec_ctx.streams.iter() {
|
||||
variables.push_str(&format!("\n {} => {}", key, value.borrow()));
|
||||
}
|
||||
|
||||
log::trace!(target: crate::log_targets::DATA_CACHE, "{}", variables);
|
||||
log::trace!(
|
||||
target: crate::log_targets::NEXT_PEER_PKS,
|
||||
" next peers pk: {:?}",
|
||||
@ -132,20 +174,15 @@ macro_rules! log_instruction {
|
||||
$exec_ctx.subtree_complete
|
||||
);
|
||||
|
||||
log::debug!(
|
||||
target: crate::log_targets::EXECUTED_TRACE,
|
||||
" current call executed trace: {:?}",
|
||||
$trace_ctx.current_trace
|
||||
);
|
||||
log::trace!(
|
||||
target: crate::log_targets::SUBTREE_ELEMENTS,
|
||||
" subtree elements count: {:?}",
|
||||
$trace_ctx.current_subtree_size
|
||||
$trace_ctx.subtree_sizes()
|
||||
);
|
||||
log::debug!(
|
||||
target: crate::log_targets::NEW_EXECUTED_TRACE,
|
||||
" new call executed trace: {:?}",
|
||||
$trace_ctx.new_trace
|
||||
$trace_ctx.as_result_trace()
|
||||
);
|
||||
};
|
||||
}
|
112
air/src/execution_step/air/next.rs
Normal file
112
air/src/execution_step/air/next.rs
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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::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 air_parser::ast::Next;
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for Next<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(next, exec_ctx, trace_ctx);
|
||||
|
||||
let iterator_name = self.0;
|
||||
let fold_state = try_get_fold_state(exec_ctx, iterator_name)?;
|
||||
maybe_meet_iteration_end(fold_state, trace_ctx)?;
|
||||
|
||||
if !fold_state.iterable.next() {
|
||||
maybe_meet_back_iterator(fold_state, trace_ctx)?;
|
||||
|
||||
// just do nothing to exit
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let next_instr = fold_state.instr_head.clone();
|
||||
maybe_meet_iteration_start(fold_state, trace_ctx)?;
|
||||
|
||||
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)?;
|
||||
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_ctx.meet_iteration_start(*fold_id, fold_state.iterable.peek().unwrap().pos())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn maybe_meet_iteration_end(fold_state: &FoldState<'_>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
if let IterableType::Stream(fold_id) = &fold_state.iterable_type {
|
||||
trace_ctx.meet_iteration_end(*fold_id)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn maybe_meet_back_iterator(fold_state: &FoldState<'_>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
if let IterableType::Stream(fold_id) = &fold_state.iterable_type {
|
||||
trace_ctx.meet_back_iterator(*fold_id)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -16,13 +16,13 @@
|
||||
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionResult;
|
||||
use super::ExecutionTraceCtx;
|
||||
use super::TraceHandler;
|
||||
use crate::log_instruction;
|
||||
|
||||
use air_parser::ast::Null;
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for Null {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(null, exec_ctx, trace_ctx);
|
||||
|
||||
Ok(())
|
111
air/src/execution_step/air/par.rs
Normal file
111
air/src/execution_step/air/par.rs
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
mod completeness_updater;
|
||||
|
||||
use super::Catchable;
|
||||
use super::ExecutableInstruction;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::Instruction;
|
||||
use super::TraceHandler;
|
||||
use crate::execution_step::trace_handler::SubtreeType;
|
||||
use crate::log_instruction;
|
||||
use completeness_updater::ParCompletenessUpdater;
|
||||
|
||||
use air_parser::ast::Par;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl<'i> ExecutableInstruction<'i> for Par<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(par, exec_ctx, trace_ctx);
|
||||
|
||||
let mut completeness_updater = ParCompletenessUpdater::new();
|
||||
trace_ctx.meet_par_start()?;
|
||||
|
||||
// execute a left subtree of par
|
||||
let left_result = execute_subtree(&self.0, exec_ctx, trace_ctx, &mut completeness_updater, SubtreeType::Left)?;
|
||||
|
||||
// execute a right subtree of par
|
||||
let right_result = execute_subtree(&self.1, exec_ctx, trace_ctx, &mut completeness_updater, SubtreeType::Right)?;
|
||||
|
||||
completeness_updater.set_completeness(exec_ctx);
|
||||
prepare_par_result(left_result, right_result, exec_ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute provided subtree and update Par state in trace_ctx.new_trace.
|
||||
fn execute_subtree<'i>(
|
||||
subtree: &Instruction<'i>,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
trace_ctx: &mut TraceHandler,
|
||||
completeness_updater: &mut ParCompletenessUpdater,
|
||||
subtree_type: SubtreeType,
|
||||
) -> ExecutionResult<SubtreeResult> {
|
||||
exec_ctx.subtree_complete = determine_subtree_complete(subtree);
|
||||
|
||||
// execute a subtree
|
||||
let result = match subtree.execute(exec_ctx, trace_ctx) {
|
||||
Ok(_) => {
|
||||
trace_ctx.meet_par_subtree_end(subtree_type)?;
|
||||
SubtreeResult::Succeeded
|
||||
}
|
||||
Err(e) if !e.is_catchable() => {
|
||||
return Err(e);
|
||||
}
|
||||
Err(e) => {
|
||||
trace_ctx.meet_par_subtree_end(subtree_type)?;
|
||||
SubtreeResult::Failed(e)
|
||||
}
|
||||
};
|
||||
|
||||
completeness_updater.update_completeness(exec_ctx, subtree_type);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
enum SubtreeResult {
|
||||
Succeeded,
|
||||
Failed(Rc<ExecutionError>),
|
||||
}
|
||||
|
||||
fn prepare_par_result(
|
||||
left_result: SubtreeResult,
|
||||
right_result: SubtreeResult,
|
||||
exec_ctx: &mut ExecutionCtx<'_>,
|
||||
) -> ExecutionResult<()> {
|
||||
match (left_result, right_result) {
|
||||
(SubtreeResult::Succeeded, _) | (_, SubtreeResult::Succeeded) => {
|
||||
// clear the last error in case of par succeeded
|
||||
exec_ctx.last_error = None;
|
||||
Ok(())
|
||||
}
|
||||
(SubtreeResult::Failed(_), SubtreeResult::Failed(err)) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_subtree_complete(next_instruction: &Instruction<'_>) -> bool {
|
||||
// this is needed to prevent situation when on such pattern
|
||||
// (fold (Iterable i
|
||||
// (par
|
||||
// (call ..)
|
||||
// (next i)
|
||||
// )
|
||||
// )
|
||||
// par will be completed after the last next that wouldn't change subtree_complete
|
||||
!matches!(next_instruction, Instruction::Next(_))
|
||||
}
|
46
air/src/execution_step/air/par/completeness_updater.rs
Normal file
46
air/src/execution_step/air/par/completeness_updater.rs
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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::SubtreeType;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(super) struct ParCompletenessUpdater {
|
||||
left_subtree_complete: bool,
|
||||
right_subtree_complete: bool,
|
||||
}
|
||||
|
||||
impl ParCompletenessUpdater {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
left_subtree_complete: false,
|
||||
right_subtree_complete: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn update_completeness(&mut self, exec_ctx: &ExecutionCtx<'_>, subtree_type: SubtreeType) {
|
||||
match subtree_type {
|
||||
SubtreeType::Left => self.left_subtree_complete = exec_ctx.subtree_complete,
|
||||
SubtreeType::Right => self.right_subtree_complete = exec_ctx.subtree_complete,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_completeness(self, exec_ctx: &mut ExecutionCtx<'_>) {
|
||||
// par is completed if at least one of its subtrees is completed
|
||||
let subtree_complete = self.left_subtree_complete || self.right_subtree_complete;
|
||||
exec_ctx.subtree_complete = subtree_complete;
|
||||
}
|
||||
}
|
@ -16,13 +16,13 @@
|
||||
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionResult;
|
||||
use super::ExecutionTraceCtx;
|
||||
use super::TraceHandler;
|
||||
use crate::log_instruction;
|
||||
|
||||
use air_parser::ast::Seq;
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for Seq<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(seq, exec_ctx, trace_ctx);
|
||||
|
||||
exec_ctx.subtree_complete = true;
|
@ -14,21 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::Catchable;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::ExecutionTraceCtx;
|
||||
use super::TraceHandler;
|
||||
use crate::log_instruction;
|
||||
|
||||
use air_parser::ast::Xor;
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for Xor<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(xor, exec_ctx, trace_ctx);
|
||||
|
||||
exec_ctx.subtree_complete = true;
|
||||
match self.0.execute(exec_ctx, trace_ctx) {
|
||||
Err(e) if is_catchable_by_xor(&e) => {
|
||||
Err(e) if e.is_catchable() => {
|
||||
exec_ctx.subtree_complete = true;
|
||||
exec_ctx.last_error_could_be_set = true;
|
||||
print_xor_log(&e);
|
||||
@ -40,16 +41,10 @@ impl<'i> super::ExecutableInstruction<'i> for Xor<'i> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true, if this execution error type should be caught by xor.
|
||||
fn is_catchable_by_xor(exec_error: &ExecutionError) -> bool {
|
||||
// this type of errors related to invalid data and should treat as hard errors.
|
||||
!matches!(exec_error, ExecutionError::InvalidExecutedState { .. })
|
||||
}
|
||||
|
||||
fn print_xor_log(e: &ExecutionError) {
|
||||
match e {
|
||||
// These errors actually aren't real errors, but a way to bubble execution up from match
|
||||
// to a corresponding xor. They'll become errors iff there is no such xor and execution is
|
||||
// These errors actually aren't real errors, but a way to bubble execution_step up from match
|
||||
// to a corresponding xor. They'll become errors iff there is no such xor and execution_step is
|
||||
// bubble up until the very beginning of current subtree. So the error message shouldn't
|
||||
// be print out in order not to confuse users.
|
||||
ExecutionError::MatchWithoutXorError | ExecutionError::MismatchWithoutXorError => {}
|
@ -21,11 +21,11 @@ mod vec_resolved_call;
|
||||
|
||||
pub(crate) use json_path_result::IterableJsonPathResult;
|
||||
pub(crate) use resolved_call::IterableResolvedCall;
|
||||
pub(crate) use vec_json_path_result::IterableVecJsonPathResult;
|
||||
pub(crate) use vec_resolved_call::IterableVecResolvedCall;
|
||||
|
||||
use super::ResolvedCallResult;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -45,6 +45,9 @@ pub(crate) trait Iterable<'ctx> {
|
||||
|
||||
/// Return current iterable value if Iterable value is not empty and None otherwise.
|
||||
fn peek(&'ctx self) -> Option<Self::Item>;
|
||||
|
||||
/// Returns length of the current iterator.
|
||||
fn len(&self) -> usize;
|
||||
}
|
||||
|
||||
/// Combines all possible iterable item types.
|
||||
@ -53,9 +56,35 @@ pub(crate) trait Iterable<'ctx> {
|
||||
/// through, i.e., it is the `iterable` in the `(fold collection iterable instruction)` statement.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum IterableItem<'ctx> {
|
||||
RefRef((&'ctx JValue, &'ctx SecurityTetraplet)),
|
||||
RefValue((&'ctx JValue, SecurityTetraplet)),
|
||||
RcValue((Rc<JValue>, SecurityTetraplet)),
|
||||
RefRef((&'ctx JValue, &'ctx RSecurityTetraplet, usize)),
|
||||
RefValue((&'ctx JValue, RSecurityTetraplet, usize)),
|
||||
RcValue((Rc<JValue>, RSecurityTetraplet, usize)),
|
||||
}
|
||||
|
||||
impl IterableItem<'_> {
|
||||
pub(crate) fn pos(&self) -> usize {
|
||||
use IterableItem::*;
|
||||
|
||||
let pos = match self {
|
||||
RefRef((.., pos)) => pos,
|
||||
RefValue((.., pos)) => pos,
|
||||
RcValue((.., pos)) => pos,
|
||||
};
|
||||
|
||||
*pos
|
||||
}
|
||||
|
||||
pub(crate) fn into_resolved_result(self) -> ResolvedCallResult {
|
||||
use IterableItem::*;
|
||||
|
||||
let (value, tetraplet, pos) = match self {
|
||||
RefRef((value, tetraplet, pos)) => (Rc::new(value.clone()), tetraplet.clone(), pos),
|
||||
RefValue((value, tetraplet, pos)) => (Rc::new(value.clone()), tetraplet, pos),
|
||||
RcValue(ingredients) => ingredients,
|
||||
};
|
||||
|
||||
ResolvedCallResult::new(value, tetraplet, pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
@ -16,22 +16,22 @@
|
||||
|
||||
use super::Iterable;
|
||||
use super::IterableItem;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::foldable_next;
|
||||
use crate::foldable_prev;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
/// Used for iterating over a result of applied to a JValue json path.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct IterableJsonPathResult {
|
||||
pub(crate) jvalues: Vec<JValue>,
|
||||
// consider adding index for each tetraplet
|
||||
pub(crate) tetraplet: SecurityTetraplet,
|
||||
pub(crate) tetraplet: RSecurityTetraplet,
|
||||
pub(crate) cursor: usize,
|
||||
}
|
||||
|
||||
impl IterableJsonPathResult {
|
||||
pub(crate) fn init(jvalues: Vec<JValue>, tetraplet: SecurityTetraplet) -> Self {
|
||||
pub(crate) fn init(jvalues: Vec<JValue>, tetraplet: RSecurityTetraplet) -> Self {
|
||||
Self {
|
||||
jvalues,
|
||||
tetraplet,
|
||||
@ -57,8 +57,12 @@ impl<'ctx> Iterable<'ctx> for IterableJsonPathResult {
|
||||
}
|
||||
|
||||
let jvalue = &self.jvalues[self.cursor];
|
||||
let result = IterableItem::RefRef((jvalue, &self.tetraplet));
|
||||
let result = IterableItem::RefRef((jvalue, &self.tetraplet, 0));
|
||||
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.jvalues.len()
|
||||
}
|
||||
}
|
@ -16,11 +16,12 @@
|
||||
|
||||
use super::Iterable;
|
||||
use super::IterableItem;
|
||||
use crate::contexts::execution::ResolvedCallResult;
|
||||
use super::ResolvedCallResult;
|
||||
use crate::foldable_next;
|
||||
use crate::foldable_prev;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Used for iterating over JValue of array type.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@ -52,25 +53,29 @@ impl<'ctx> Iterable<'ctx> for IterableResolvedCall {
|
||||
}
|
||||
|
||||
fn peek(&'ctx self) -> Option<Self::Item> {
|
||||
use std::ops::Deref;
|
||||
|
||||
if self.len == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let triplet = self.call_result.triplet.clone();
|
||||
let tetraplet = SecurityTetraplet {
|
||||
triplet,
|
||||
// TODO: consider set json_path to the current cursor here
|
||||
json_path: String::new(),
|
||||
};
|
||||
let ResolvedCallResult {
|
||||
result,
|
||||
tetraplet,
|
||||
trace_pos,
|
||||
} = &self.call_result;
|
||||
|
||||
let jvalue = match &self.call_result.result.deref() {
|
||||
let jvalue = match &result.deref() {
|
||||
JValue::Array(array) => &array[self.cursor],
|
||||
_ => unimplemented!("this jvalue is set only by fold instruction, so it must have an array type"),
|
||||
};
|
||||
|
||||
let result = IterableItem::RefValue((jvalue, tetraplet));
|
||||
let result = IterableItem::RefValue((jvalue, tetraplet.clone(), *trace_pos));
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
match self.call_result.result.deref() {
|
||||
JValue::Array(array) => array.len(),
|
||||
_ => unimplemented!("this jvalue is set only by fold instruction, so it must have an array type"),
|
||||
}
|
||||
}
|
||||
}
|
@ -16,21 +16,22 @@
|
||||
|
||||
use super::Iterable;
|
||||
use super::IterableItem;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
use crate::foldable_next;
|
||||
use crate::foldable_prev;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
/// Used for iterating over a result of applied to an stream json path.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct IterableVecJsonPathResult {
|
||||
pub(crate) jvalues: Vec<JValue>,
|
||||
pub(crate) tetraplets: Vec<SecurityTetraplet>,
|
||||
pub(crate) tetraplets: SecurityTetraplets,
|
||||
pub(crate) cursor: usize,
|
||||
}
|
||||
|
||||
impl IterableVecJsonPathResult {
|
||||
pub(crate) fn init(jvalues: Vec<JValue>, tetraplets: Vec<SecurityTetraplet>) -> Self {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn init(jvalues: Vec<JValue>, tetraplets: SecurityTetraplets) -> Self {
|
||||
// TODO: add assert on length
|
||||
Self {
|
||||
jvalues,
|
||||
@ -58,8 +59,12 @@ impl<'ctx> Iterable<'ctx> for IterableVecJsonPathResult {
|
||||
|
||||
let jvalue = &self.jvalues[self.cursor];
|
||||
let tetraplet = &self.tetraplets[self.cursor];
|
||||
let result = IterableItem::RefRef((jvalue, tetraplet));
|
||||
let result = IterableItem::RefRef((jvalue, tetraplet, 0));
|
||||
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.jvalues.len()
|
||||
}
|
||||
}
|
@ -16,10 +16,9 @@
|
||||
|
||||
use super::Iterable;
|
||||
use super::IterableItem;
|
||||
use crate::contexts::execution::ResolvedCallResult;
|
||||
use super::ResolvedCallResult;
|
||||
use crate::foldable_next;
|
||||
use crate::foldable_prev;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
/// Used for iterating over stream with JValues.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@ -53,13 +52,17 @@ impl<'ctx> Iterable<'ctx> for IterableVecResolvedCall {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ResolvedCallResult { result, triplet } = self.call_results[self.cursor].clone();
|
||||
let tetraplet = SecurityTetraplet {
|
||||
triplet,
|
||||
json_path: String::new(),
|
||||
};
|
||||
let ResolvedCallResult {
|
||||
result,
|
||||
tetraplet,
|
||||
trace_pos,
|
||||
} = &self.call_results[self.cursor];
|
||||
|
||||
let result = IterableItem::RcValue((result, tetraplet));
|
||||
let result = IterableItem::RcValue((result.clone(), tetraplet.clone(), *trace_pos));
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.call_results.len()
|
||||
}
|
||||
}
|
@ -18,12 +18,16 @@ mod cell_vec_resolved_call_result;
|
||||
mod empty;
|
||||
mod iterable_item;
|
||||
mod resolved_call_result;
|
||||
mod stream;
|
||||
|
||||
use super::iterable::IterableItem;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::ResolvedCallResult;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
pub(crate) use stream::StreamJvaluableIngredients;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
@ -33,10 +37,7 @@ pub(crate) trait JValuable {
|
||||
fn apply_json_path(&self, json_path: &str) -> ExecutionResult<Vec<&JValue>>;
|
||||
|
||||
/// Applies json path to the internal value, produces JValue with tetraplet.
|
||||
fn apply_json_path_with_tetraplets(
|
||||
&self,
|
||||
json_path: &str,
|
||||
) -> ExecutionResult<(Vec<&JValue>, Vec<SecurityTetraplet>)>;
|
||||
fn apply_json_path_with_tetraplets(&self, json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)>;
|
||||
|
||||
/// Return internal value as borrowed if it's possible, owned otherwise.
|
||||
fn as_jvalue(&self) -> Cow<'_, JValue>;
|
||||
@ -45,5 +46,5 @@ pub(crate) trait JValuable {
|
||||
fn into_jvalue(self: Box<Self>) -> JValue;
|
||||
|
||||
/// Return tetraplets associating with internal value.
|
||||
fn as_tetraplets(&self) -> Vec<SecurityTetraplet>;
|
||||
fn as_tetraplets(&self) -> SecurityTetraplets;
|
||||
}
|
@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::ExecutionError::JValueStreamJsonPathError;
|
||||
use super::ExecutionError::GenerationStreamJsonPathError;
|
||||
use super::ExecutionResult;
|
||||
use super::JValuable;
|
||||
use crate::contexts::execution::ResolvedCallResult;
|
||||
use super::ResolvedCallResult;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use jsonpath_lib::select_with_iter;
|
||||
|
||||
@ -30,27 +30,25 @@ impl JValuable for std::cell::Ref<'_, Vec<ResolvedCallResult>> {
|
||||
fn apply_json_path(&self, json_path: &str) -> ExecutionResult<Vec<&JValue>> {
|
||||
let acc_iter = self.iter().map(|r| r.result.deref());
|
||||
let (selected_values, _) = select_with_iter(acc_iter, json_path).map_err(|e| {
|
||||
JValueStreamJsonPathError(self.iter().cloned().collect::<Vec<_>>(), json_path.to_string(), e)
|
||||
GenerationStreamJsonPathError(self.iter().cloned().collect::<Vec<_>>(), json_path.to_string(), e)
|
||||
})?;
|
||||
|
||||
Ok(selected_values)
|
||||
}
|
||||
|
||||
fn apply_json_path_with_tetraplets(
|
||||
&self,
|
||||
json_path: &str,
|
||||
) -> ExecutionResult<(Vec<&JValue>, Vec<SecurityTetraplet>)> {
|
||||
fn apply_json_path_with_tetraplets(&self, json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)> {
|
||||
let acc_iter = self.iter().map(|r| r.result.deref());
|
||||
|
||||
let (selected_values, tetraplet_indices) = select_with_iter(acc_iter, json_path).map_err(|e| {
|
||||
JValueStreamJsonPathError(self.iter().cloned().collect::<Vec<_>>(), json_path.to_string(), e)
|
||||
GenerationStreamJsonPathError(self.iter().cloned().collect::<Vec<_>>(), json_path.to_string(), e)
|
||||
})?;
|
||||
|
||||
let tetraplets = tetraplet_indices
|
||||
.into_iter()
|
||||
.map(|id| SecurityTetraplet {
|
||||
triplet: self[id].triplet.clone(),
|
||||
json_path: json_path.to_string(),
|
||||
.map(|id| {
|
||||
let tetraplet = self[id].tetraplet.clone();
|
||||
tetraplet.borrow_mut().add_json_path(json_path);
|
||||
tetraplet
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -67,12 +65,7 @@ impl JValuable for std::cell::Ref<'_, Vec<ResolvedCallResult>> {
|
||||
JValue::Array(jvalue_array)
|
||||
}
|
||||
|
||||
fn as_tetraplets(&self) -> Vec<SecurityTetraplet> {
|
||||
self.iter()
|
||||
.map(|r| SecurityTetraplet {
|
||||
triplet: r.triplet.clone(),
|
||||
json_path: String::new(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
fn as_tetraplets(&self) -> SecurityTetraplets {
|
||||
self.iter().map(|r| r.tetraplet.clone()).collect::<Vec<_>>()
|
||||
}
|
||||
}
|
@ -16,8 +16,8 @@
|
||||
|
||||
use super::ExecutionResult;
|
||||
use super::JValuable;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
@ -26,10 +26,7 @@ impl JValuable for () {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn apply_json_path_with_tetraplets(
|
||||
&self,
|
||||
_json_path: &str,
|
||||
) -> ExecutionResult<(Vec<&JValue>, Vec<SecurityTetraplet>)> {
|
||||
fn apply_json_path_with_tetraplets(&self, _json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)> {
|
||||
Ok((vec![], vec![]))
|
||||
}
|
||||
|
||||
@ -41,7 +38,7 @@ impl JValuable for () {
|
||||
JValue::Array(vec![])
|
||||
}
|
||||
|
||||
fn as_tetraplets(&self) -> Vec<SecurityTetraplet> {
|
||||
fn as_tetraplets(&self) -> SecurityTetraplets {
|
||||
vec![]
|
||||
}
|
||||
}
|
@ -18,8 +18,8 @@ use super::ExecutionError::JValueJsonPathError as JsonPathError;
|
||||
use super::ExecutionResult;
|
||||
use super::IterableItem;
|
||||
use super::JValuable;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use jsonpath_lib::select;
|
||||
|
||||
@ -31,9 +31,9 @@ impl<'ctx> JValuable for IterableItem<'ctx> {
|
||||
use super::IterableItem::*;
|
||||
|
||||
let jvalue = match self {
|
||||
RefRef((jvalue, _)) => *jvalue,
|
||||
RefValue((jvalue, _)) => jvalue,
|
||||
RcValue((jvalue, _)) => jvalue.deref(),
|
||||
RefRef((jvalue, ..)) => *jvalue,
|
||||
RefValue((jvalue, ..)) => jvalue,
|
||||
RcValue((jvalue, ..)) => jvalue.deref(),
|
||||
};
|
||||
|
||||
let selected_jvalues =
|
||||
@ -41,16 +41,13 @@ impl<'ctx> JValuable for IterableItem<'ctx> {
|
||||
Ok(selected_jvalues)
|
||||
}
|
||||
|
||||
fn apply_json_path_with_tetraplets(
|
||||
&self,
|
||||
json_path: &str,
|
||||
) -> ExecutionResult<(Vec<&JValue>, Vec<SecurityTetraplet>)> {
|
||||
fn apply_json_path_with_tetraplets(&self, json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)> {
|
||||
use super::IterableItem::*;
|
||||
|
||||
let (jvalue, tetraplet) = match self {
|
||||
RefRef((jvalue, tetraplet)) => (*jvalue, *tetraplet),
|
||||
RefValue((jvalue, tetraplet)) => (*jvalue, tetraplet),
|
||||
RcValue((jvalue, tetraplet)) => (jvalue.deref(), tetraplet),
|
||||
RefRef((jvalue, tetraplet, _)) => (*jvalue, *tetraplet),
|
||||
RefValue((jvalue, tetraplet, _)) => (*jvalue, tetraplet),
|
||||
RcValue((jvalue, tetraplet, _)) => (jvalue.deref(), tetraplet),
|
||||
};
|
||||
|
||||
let selected_jvalues =
|
||||
@ -62,9 +59,9 @@ impl<'ctx> JValuable for IterableItem<'ctx> {
|
||||
use super::IterableItem::*;
|
||||
|
||||
match self {
|
||||
RefRef((jvalue, _)) => Cow::Borrowed(jvalue),
|
||||
RefValue((jvalue, _)) => Cow::Borrowed(jvalue),
|
||||
RcValue((jvalue, _)) => Cow::Borrowed(jvalue.deref()),
|
||||
RefRef((jvalue, ..)) => Cow::Borrowed(jvalue),
|
||||
RefValue((jvalue, ..)) => Cow::Borrowed(jvalue),
|
||||
RcValue((jvalue, ..)) => Cow::Borrowed(jvalue.deref()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,23 +69,23 @@ impl<'ctx> JValuable for IterableItem<'ctx> {
|
||||
use super::IterableItem::*;
|
||||
|
||||
match *self {
|
||||
RefRef((jvalue, _)) => jvalue.deref().clone(),
|
||||
RefValue((jvalue, _)) => jvalue.clone(),
|
||||
RcValue((jvalue, _)) => jvalue.deref().clone(),
|
||||
RefRef((jvalue, ..)) => jvalue.deref().clone(),
|
||||
RefValue((jvalue, ..)) => jvalue.clone(),
|
||||
RcValue((jvalue, ..)) => jvalue.deref().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_tetraplets(&self) -> Vec<SecurityTetraplet> {
|
||||
fn as_tetraplets(&self) -> SecurityTetraplets {
|
||||
use super::IterableItem::*;
|
||||
|
||||
// these clones are needed because rust-sdk allows passing arguments only by value
|
||||
match self {
|
||||
RefRef((_, tetraplet)) => {
|
||||
RefRef((_, tetraplet, _)) => {
|
||||
let tetraplet = tetraplet.deref().clone();
|
||||
vec![tetraplet]
|
||||
}
|
||||
RefValue((_, tetraplet)) => vec![(*tetraplet).clone()],
|
||||
RcValue((_, tetraplet)) => vec![(*tetraplet).clone()],
|
||||
RefValue((_, tetraplet, _)) => vec![tetraplet.clone()],
|
||||
RcValue((_, tetraplet, _)) => vec![tetraplet.clone()],
|
||||
}
|
||||
}
|
||||
}
|
@ -16,9 +16,9 @@
|
||||
|
||||
use super::ExecutionResult;
|
||||
use super::JValuable;
|
||||
use crate::contexts::execution::ResolvedCallResult;
|
||||
use super::ResolvedCallResult;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use jsonpath_lib::select;
|
||||
|
||||
@ -34,20 +34,15 @@ impl JValuable for ResolvedCallResult {
|
||||
Ok(selected_jvalues)
|
||||
}
|
||||
|
||||
fn apply_json_path_with_tetraplets(
|
||||
&self,
|
||||
json_path: &str,
|
||||
) -> ExecutionResult<(Vec<&JValue>, Vec<SecurityTetraplet>)> {
|
||||
fn apply_json_path_with_tetraplets(&self, json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)> {
|
||||
use super::ExecutionError::JValueJsonPathError as JsonPathError;
|
||||
|
||||
is_json_path_allowed(&self.result)?;
|
||||
let selected_jvalues = select(&self.result, json_path)
|
||||
.map_err(|e| JsonPathError(self.result.deref().clone(), String::from(json_path), e))?;
|
||||
|
||||
let tetraplet = SecurityTetraplet {
|
||||
triplet: self.triplet.clone(),
|
||||
json_path: json_path.to_string(),
|
||||
};
|
||||
let tetraplet = self.tetraplet.clone();
|
||||
tetraplet.borrow_mut().add_json_path(json_path);
|
||||
|
||||
Ok((selected_jvalues, vec![tetraplet]))
|
||||
}
|
||||
@ -60,13 +55,8 @@ impl JValuable for ResolvedCallResult {
|
||||
self.result.deref().clone()
|
||||
}
|
||||
|
||||
fn as_tetraplets(&self) -> Vec<SecurityTetraplet> {
|
||||
let tetraplet = SecurityTetraplet {
|
||||
triplet: self.triplet.clone(),
|
||||
json_path: String::new(),
|
||||
};
|
||||
|
||||
vec![tetraplet]
|
||||
fn as_tetraplets(&self) -> SecurityTetraplets {
|
||||
vec![self.tetraplet.clone()]
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,8 +65,8 @@ fn is_json_path_allowed(value: &JValue) -> ExecutionResult<()> {
|
||||
use crate::exec_err;
|
||||
|
||||
match value {
|
||||
JValue::Array(_) => return Ok(()),
|
||||
JValue::Object(_) => return Ok(()),
|
||||
JValue::Array(_) => Ok(()),
|
||||
JValue::Object(_) => Ok(()),
|
||||
value => exec_err!(ExecutionError::JsonPathVariableTypeError(value.clone())),
|
||||
}
|
||||
}
|
110
air/src/execution_step/boxed_value/jvaluable/stream.rs
Normal file
110
air/src/execution_step/boxed_value/jvaluable/stream.rs
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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::ExecutionError::StreamJsonPathError;
|
||||
use super::ExecutionResult;
|
||||
use super::JValuable;
|
||||
use crate::exec_err;
|
||||
use crate::execution_step::boxed_value::Generation;
|
||||
use crate::execution_step::boxed_value::Stream;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
use crate::JValue;
|
||||
|
||||
use jsonpath_lib::select_with_iter;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct StreamJvaluableIngredients<'stream> {
|
||||
pub(crate) stream: std::cell::Ref<'stream, Stream>,
|
||||
pub(crate) generation: Generation,
|
||||
}
|
||||
|
||||
// TODO: this will be deleted soon, because it would be impossible to use streams without
|
||||
// canonicalization as an arg of a call
|
||||
impl JValuable for StreamJvaluableIngredients<'_> {
|
||||
fn apply_json_path(&self, json_path: &str) -> ExecutionResult<Vec<&JValue>> {
|
||||
let iter = self.iter()?.map(|v| v.result.deref());
|
||||
|
||||
let (selected_values, _) = select_with_iter(iter, json_path)
|
||||
.map_err(|e| StreamJsonPathError(self.stream.deref().clone(), json_path.to_string(), e))?;
|
||||
|
||||
Ok(selected_values)
|
||||
}
|
||||
|
||||
fn apply_json_path_with_tetraplets(&self, json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)> {
|
||||
let iter = self.iter()?.map(|v| v.result.deref());
|
||||
|
||||
let (selected_values, tetraplet_indices) = select_with_iter(iter, json_path)
|
||||
.map_err(|e| StreamJsonPathError(self.stream.deref().clone(), json_path.to_string(), e))?;
|
||||
|
||||
let mut tetraplets = Vec::with_capacity(tetraplet_indices.len());
|
||||
|
||||
for idx in tetraplet_indices.iter() {
|
||||
let resolved_call = self.iter()?.nth(*idx).unwrap();
|
||||
let tetraplet = resolved_call.tetraplet.clone();
|
||||
tetraplet.borrow_mut().add_json_path(json_path);
|
||||
tetraplets.push(tetraplet);
|
||||
}
|
||||
|
||||
Ok((selected_values, tetraplets))
|
||||
}
|
||||
|
||||
fn as_jvalue(&self) -> Cow<'_, JValue> {
|
||||
let jvalue = self.stream.deref().clone().as_jvalue(self.generation).unwrap();
|
||||
Cow::Owned(jvalue)
|
||||
}
|
||||
|
||||
fn into_jvalue(self: Box<Self>) -> JValue {
|
||||
self.stream.as_jvalue(self.generation).unwrap()
|
||||
}
|
||||
|
||||
fn as_tetraplets(&self) -> SecurityTetraplets {
|
||||
self.stream
|
||||
.iter(self.generation)
|
||||
.unwrap()
|
||||
.map(|r| r.tetraplet.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
use crate::execution_step::boxed_value::StreamIter;
|
||||
|
||||
impl<'stream> StreamJvaluableIngredients<'stream> {
|
||||
pub(crate) fn new(stream: std::cell::Ref<'stream, Stream>, generation: Generation) -> Self {
|
||||
Self { stream, generation }
|
||||
}
|
||||
|
||||
pub(self) fn iter(&self) -> ExecutionResult<StreamIter<'_>> {
|
||||
use super::ExecutionError::StreamDontHaveSuchGeneration;
|
||||
|
||||
match self.stream.iter(self.generation) {
|
||||
Some(iter) => Ok(iter),
|
||||
None => {
|
||||
let generation = match self.generation {
|
||||
Generation::Nth(generation) => generation,
|
||||
Generation::Last => unreachable!(),
|
||||
};
|
||||
|
||||
exec_err!(StreamDontHaveSuchGeneration(
|
||||
self.stream.deref().clone(),
|
||||
generation as usize
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
air/src/execution_step/boxed_value/mod.rs
Normal file
33
air/src/execution_step/boxed_value/mod.rs
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
mod iterable;
|
||||
mod jvaluable;
|
||||
mod scalar;
|
||||
mod stream;
|
||||
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 stream::Generation;
|
||||
pub(crate) use stream::Stream;
|
||||
pub(crate) use stream::StreamIter;
|
||||
pub(crate) use variable::Variable;
|
||||
|
||||
use super::ExecutionResult;
|
@ -14,14 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::execution::FoldState;
|
||||
use super::JValuable;
|
||||
use crate::execution_step::FoldState;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::JValue;
|
||||
use crate::ResolvedTriplet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::rc::Rc;
|
||||
@ -29,28 +29,44 @@ use std::rc::Rc;
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ResolvedCallResult {
|
||||
pub result: Rc<JValue>,
|
||||
pub triplet: Rc<ResolvedTriplet>,
|
||||
pub tetraplet: RSecurityTetraplet,
|
||||
pub trace_pos: usize,
|
||||
}
|
||||
|
||||
pub(crate) enum AValue<'i> {
|
||||
pub(crate) enum Scalar<'i> {
|
||||
JValueRef(ResolvedCallResult),
|
||||
JValueStreamRef(RefCell<Vec<ResolvedCallResult>>),
|
||||
JValueFoldCursor(FoldState<'i>),
|
||||
}
|
||||
|
||||
impl<'i> Display for AValue<'i> {
|
||||
impl<'i> Scalar<'i> {
|
||||
pub(crate) fn to_jvaluable<'ctx>(&'ctx self) -> Box<dyn JValuable + 'ctx> {
|
||||
match self {
|
||||
Scalar::JValueRef(value) => Box::new(value.clone()),
|
||||
Scalar::JValueFoldCursor(fold_state) => {
|
||||
let peeked_value = fold_state.iterable.peek().unwrap();
|
||||
Box::new(peeked_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResolvedCallResult {
|
||||
pub(crate) fn new(result: Rc<JValue>, tetraplet: RSecurityTetraplet, trace_pos: usize) -> Self {
|
||||
Self {
|
||||
result,
|
||||
tetraplet,
|
||||
trace_pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Display for Scalar<'i> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AValue::JValueRef(value) => write!(f, "{:?}", value)?,
|
||||
AValue::JValueStreamRef(stream) => {
|
||||
write!(f, "[ ")?;
|
||||
for value in stream.borrow().iter() {
|
||||
write!(f, "{:?} ", value)?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
}
|
||||
AValue::JValueFoldCursor(_) => {
|
||||
write!(f, "cursor")?;
|
||||
Scalar::JValueRef(value) => write!(f, "{:?}", value)?,
|
||||
Scalar::JValueFoldCursor(cursor) => {
|
||||
let iterable = &cursor.iterable;
|
||||
write!(f, "cursor, current value: {:?}", iterable.peek())?;
|
||||
}
|
||||
}
|
||||
|
205
air/src/execution_step/boxed_value/stream.rs
Normal file
205
air/src/execution_step/boxed_value/stream.rs
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::ResolvedCallResult;
|
||||
use crate::exec_err;
|
||||
use crate::JValue;
|
||||
|
||||
use std::fmt::Formatter;
|
||||
|
||||
/// Streams are CRDT-like append only data structures. They are guaranteed to have the same order
|
||||
/// of values on each peer.
|
||||
///
|
||||
/// The first Vec represents generations, the second values in a generation. Generation is a set
|
||||
/// of values that interpreter obtained from one particle. It means that number of generation on
|
||||
/// a peer is equal to number of the interpreter runs in context of one particle. And each set of
|
||||
/// 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>>);
|
||||
|
||||
impl Stream {
|
||||
pub(crate) fn from_generations_count(count: usize) -> Self {
|
||||
Self(vec![vec![]; count + 1])
|
||||
}
|
||||
|
||||
pub(crate) fn from_value(value: ResolvedCallResult) -> 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> {
|
||||
let generation = match generation {
|
||||
Generation::Last => self.0.len() - 1,
|
||||
Generation::Nth(id) => id as usize,
|
||||
};
|
||||
|
||||
if generation >= self.0.len() {
|
||||
return exec_err!(ExecutionError::StreamDontHaveSuchGeneration(self.clone(), generation));
|
||||
}
|
||||
|
||||
self.0[generation].push(value);
|
||||
Ok(generation as u32)
|
||||
}
|
||||
|
||||
pub(crate) fn generations_count(&self) -> usize {
|
||||
let generations_count = self.0.len();
|
||||
|
||||
// the last generation could be empty due to the logic of from_generations_count ctor
|
||||
if generations_count > 0 && self.0[generations_count - 1].is_empty() {
|
||||
generations_count - 1
|
||||
} else {
|
||||
generations_count
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn elements_count(&self, generation: Generation) -> Option<usize> {
|
||||
match generation {
|
||||
Generation::Nth(generation) if generation as usize > self.generations_count() => None,
|
||||
Generation::Nth(generation) => Some(self.0.iter().take(generation as usize).map(|v| v.len()).sum()),
|
||||
Generation::Last => Some(self.0.iter().map(|v| v.len()).sum()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
if self.0.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.0.iter().all(|v| v.is_empty())
|
||||
}
|
||||
|
||||
pub(crate) fn as_jvalue(&self, generation: Generation) -> Option<JValue> {
|
||||
use std::ops::Deref;
|
||||
|
||||
let iter = self.iter(generation)?;
|
||||
let jvalue_array = iter.map(|r| r.result.deref().clone()).collect::<Vec<_>>();
|
||||
|
||||
Some(JValue::Array(jvalue_array))
|
||||
}
|
||||
|
||||
pub(crate) fn iter(&self, generation: Generation) -> Option<StreamIter<'_>> {
|
||||
let iter: Box<dyn Iterator<Item = &ResolvedCallResult>> = 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())),
|
||||
};
|
||||
// unwrap is safe here, because generation's been already checked
|
||||
let len = self.elements_count(generation).unwrap();
|
||||
|
||||
let iter = StreamIter { iter, len };
|
||||
|
||||
Some(iter)
|
||||
}
|
||||
|
||||
pub(crate) fn slice_iter(&self, generation: Generation) -> Option<StreamSliceIter<'_>> {
|
||||
let iter: Box<dyn Iterator<Item = &[ResolvedCallResult]>> = 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())),
|
||||
};
|
||||
|
||||
let len = match generation {
|
||||
Generation::Nth(generation) => generation as usize,
|
||||
Generation::Last => self.0.len(),
|
||||
};
|
||||
|
||||
let iter = StreamSliceIter { iter, len };
|
||||
|
||||
Some(iter)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum Generation {
|
||||
Last,
|
||||
Nth(u32),
|
||||
}
|
||||
|
||||
impl Generation {
|
||||
pub(crate) fn from_option(raw_generation: Option<u32>) -> Self {
|
||||
match raw_generation {
|
||||
Some(generation) => Generation::Nth(generation),
|
||||
None => Generation::Last,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct StreamIter<'result> {
|
||||
iter: Box<dyn Iterator<Item = &'result ResolvedCallResult> + 'result>,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<'result> Iterator for StreamIter<'result> {
|
||||
type Item = &'result ResolvedCallResult;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.len > 0 {
|
||||
self.len -= 1;
|
||||
}
|
||||
self.iter.next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.len, Some(self.len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'result> ExactSizeIterator for StreamIter<'result> {}
|
||||
|
||||
pub(crate) struct StreamSliceIter<'slice> {
|
||||
iter: Box<dyn Iterator<Item = &'slice [ResolvedCallResult]> + 'slice>,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<'slice> Iterator for StreamSliceIter<'slice> {
|
||||
type Item = &'slice [ResolvedCallResult];
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.len > 0 {
|
||||
self.len -= 1;
|
||||
}
|
||||
self.iter.next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.len, Some(self.len))
|
||||
}
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for Stream {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
if self.0.is_empty() {
|
||||
return write!(f, "[]");
|
||||
}
|
||||
|
||||
write!(f, "[ ")?;
|
||||
for (id, generation) in self.0.iter().enumerate() {
|
||||
write!(f, " -- {}: ", id)?;
|
||||
for value in generation.iter() {
|
||||
write!(f, "{:?}, ", value)?;
|
||||
}
|
||||
writeln!(f)?;
|
||||
}
|
||||
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
49
air/src/execution_step/boxed_value/variable.rs
Normal file
49
air/src/execution_step/boxed_value/variable.rs
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2020 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::Generation;
|
||||
use air_parser::ast::AstVariable;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) enum Variable<'i> {
|
||||
Scalar(&'i str),
|
||||
Stream { name: &'i str, generation: Generation },
|
||||
}
|
||||
|
||||
impl<'i> Variable<'i> {
|
||||
pub(crate) fn from_ast(ast_variable: &AstVariable<'i>) -> Self {
|
||||
match ast_variable {
|
||||
AstVariable::Scalar(name) => Variable::Scalar(name),
|
||||
AstVariable::Stream(name) => Variable::Stream {
|
||||
name,
|
||||
generation: Generation::Last,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_ast_with_generation(ast_variable: &AstVariable<'i>, generation: Generation) -> Self {
|
||||
match ast_variable {
|
||||
AstVariable::Scalar(name) => Variable::Scalar(name),
|
||||
AstVariable::Stream(name) => Variable::Stream { name, generation },
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_stream(name: &'i str, generation: Generation) -> Self {
|
||||
Self::Stream { name, generation }
|
||||
}
|
||||
}
|
@ -14,11 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod catchable;
|
||||
|
||||
pub(crate) use catchable::Catchable;
|
||||
|
||||
use super::trace_handler::MergerApResult;
|
||||
use super::trace_handler::TraceHandlerError;
|
||||
use super::Joinable;
|
||||
use super::ResolvedCallResult;
|
||||
use super::Stream;
|
||||
use crate::build_targets::CallServiceResult;
|
||||
use crate::contexts::execution::ResolvedCallResult;
|
||||
use crate::contexts::execution_trace::ExecutedState;
|
||||
use crate::contexts::execution_trace::ExecutionTrace;
|
||||
use crate::JValue;
|
||||
|
||||
use jsonpath_lib::JsonPathError;
|
||||
@ -34,9 +39,9 @@ pub(crate) enum ExecutionError {
|
||||
#[error("call_service result '{0}' can't be serialized or deserialized with an error: {1}")]
|
||||
CallServiceResultDeError(CallServiceResult, SerdeJsonError),
|
||||
|
||||
/// Semantic errors in instructions.
|
||||
#[error("{0}")]
|
||||
InstructionError(String),
|
||||
/// Semantic errors in a call instructions.
|
||||
#[error("call should have service id specified by peer part or function part")]
|
||||
IncorrectCallTriplet,
|
||||
|
||||
/// An error is occurred while calling local service via call_service.
|
||||
#[error("Local service error, ret_code is {0}, error message is '{1}'")]
|
||||
@ -54,9 +59,13 @@ pub(crate) enum ExecutionError {
|
||||
#[error("variable with path '{1}' not found in '{0}' with an error: '{2}'")]
|
||||
JValueJsonPathError(JValue, String, JsonPathError),
|
||||
|
||||
/// An error occurred while trying to apply json path to this stream generation with JValue's.
|
||||
#[error("variable with path '{1}' not found in '{0:?}' with error: '{2}'")]
|
||||
GenerationStreamJsonPathError(Vec<ResolvedCallResult>, String, JsonPathError),
|
||||
|
||||
/// An error occurred while trying to apply json path to this stream with JValue's.
|
||||
#[error("variable with path '{1}' not found in '{0:?}' with error: '{2}'")]
|
||||
JValueStreamJsonPathError(Vec<ResolvedCallResult>, String, JsonPathError),
|
||||
StreamJsonPathError(Stream, String, JsonPathError),
|
||||
|
||||
/// Provided JValue has incompatible with target type.
|
||||
#[error("expected JValue type '{1}', but got '{0}' JValue")]
|
||||
@ -78,35 +87,9 @@ pub(crate) enum ExecutionError {
|
||||
#[error("multiple fold states found for iterable '{0}'")]
|
||||
MultipleFoldStates(String),
|
||||
|
||||
/// Expected executed state of a different type.
|
||||
#[error(
|
||||
r#"
|
||||
======================================================================
|
||||
Below is a crucial debug information, please send this to the Fluence Labs:
|
||||
|
||||
invalid executed state error:
|
||||
current instruction: {instruction},
|
||||
expected state: {expected_state},
|
||||
actual_state: '{actual_state}',
|
||||
current_trace: '{current_trace:?}',
|
||||
new_trace: '{new_trace:?}',
|
||||
current_subtree_size: {current_subtree_size},
|
||||
|
||||
======================================================================
|
||||
"#
|
||||
)]
|
||||
InvalidExecutedState {
|
||||
instruction: String,
|
||||
expected_state: &'static str,
|
||||
actual_state: ExecutedState,
|
||||
current_trace: ExecutionTrace,
|
||||
new_trace: ExecutionTrace,
|
||||
current_subtree_size: usize,
|
||||
},
|
||||
|
||||
/// Errors encountered while shadowing non-scalar values.
|
||||
#[error("variable with name '{0}' can't be shadowed, shadowing is supported only for scalar values")]
|
||||
ShadowingError(String),
|
||||
#[error("variable with name '{0}' can't be shadowed, shadowing isn't supported for iterables")]
|
||||
IterableShadowing(String),
|
||||
|
||||
/// This error type is produced by a match to notify xor that compared values aren't equal.
|
||||
#[error("match is used without corresponding xor")]
|
||||
@ -126,6 +109,25 @@ pub(crate) enum ExecutionError {
|
||||
it could be applied only to streams and variables of array and object types"
|
||||
)]
|
||||
JsonPathVariableTypeError(JValue),
|
||||
|
||||
/// Errors bubbled from a trace handler.
|
||||
#[error("{0}")]
|
||||
TraceError(#[from] TraceHandlerError),
|
||||
|
||||
/// Errors occurred while insertion of a value inside stream that doesn't have corresponding generation.
|
||||
#[error("stream {0:?} doesn't have generation with number {1}, probably a supplied to the interpreter data is corrupted")]
|
||||
StreamDontHaveSuchGeneration(Stream, usize),
|
||||
|
||||
/// Errors occurred when result from data doesn't match to a instruction, f.e. an instruction
|
||||
/// could be applied to a stream, but result doesn't contain generation in a source position.
|
||||
#[error("ap result {0:?} doesn't match corresponding instruction")]
|
||||
ApResultNotCorrespondToInstr(MergerApResult),
|
||||
}
|
||||
|
||||
impl From<TraceHandlerError> for Rc<ExecutionError> {
|
||||
fn from(trace_error: TraceHandlerError) -> Self {
|
||||
Rc::new(ExecutionError::TraceError(trace_error))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecutionError {
|
||||
@ -134,23 +136,26 @@ impl ExecutionError {
|
||||
|
||||
match self {
|
||||
CallServiceResultDeError(..) => 1,
|
||||
InstructionError(_) => 2,
|
||||
IncorrectCallTriplet => 2,
|
||||
LocalServiceError(..) => 3,
|
||||
VariableNotFound(_) => 4,
|
||||
MultipleVariablesFound(_) => 5,
|
||||
JValueJsonPathError(..) => 6,
|
||||
JValueStreamJsonPathError(..) => 7,
|
||||
GenerationStreamJsonPathError(..) => 7,
|
||||
IncompatibleJValueType(..) => 8,
|
||||
IncompatibleAValueType(..) => 9,
|
||||
MultipleValuesInJsonPath(_) => 10,
|
||||
FoldStateNotFound(_) => 11,
|
||||
MultipleFoldStates(_) => 12,
|
||||
InvalidExecutedState { .. } => 13,
|
||||
ShadowingError(_) => 14,
|
||||
MatchWithoutXorError => 15,
|
||||
MismatchWithoutXorError => 16,
|
||||
FlatteningError(_) => 17,
|
||||
JsonPathVariableTypeError(_) => 18,
|
||||
IterableShadowing(_) => 13,
|
||||
MatchWithoutXorError => 14,
|
||||
MismatchWithoutXorError => 15,
|
||||
FlatteningError(_) => 16,
|
||||
JsonPathVariableTypeError(_) => 17,
|
||||
StreamJsonPathError(..) => 18,
|
||||
StreamDontHaveSuchGeneration(..) => 19,
|
||||
ApResultNotCorrespondToInstr(_) => 20,
|
||||
TraceError(_) => 21,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -173,15 +178,23 @@ impl Joinable for ExecutionError {
|
||||
log_join!(" waiting for an argument with name '{}'", var_name);
|
||||
true
|
||||
}
|
||||
JValueStreamJsonPathError(stream, json_path, _) => {
|
||||
StreamJsonPathError(stream, json_path, _) => {
|
||||
log_join!(" waiting for an argument with path '{}' on stream '{:?}'", json_path, stream);
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Catchable for ExecutionError {
|
||||
fn is_catchable(&self) -> bool {
|
||||
// this kind is related to an invalid data and should treat as a non-catchable error
|
||||
!matches!(self, ExecutionError::TraceError(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for ExecutionError {
|
||||
fn from(_: std::convert::Infallible) -> Self {
|
||||
unreachable!()
|
24
air/src/execution_step/errors/catchable.rs
Normal file
24
air/src/execution_step/errors/catchable.rs
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/// This trait is intended to differentiate between catchable and non-catchable error types.
|
||||
/// Errors of the first type could be caught by xor, the second couldn't and should stop
|
||||
/// AIR execution. This is needed to prevent some malicious data merging and manage
|
||||
/// prev_data always in a valid state.
|
||||
pub(crate) trait Catchable {
|
||||
/// Return true, if error is catchable.
|
||||
fn is_catchable(&self) -> bool;
|
||||
}
|
@ -14,32 +14,33 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod avalue;
|
||||
pub(crate) mod error_descriptor;
|
||||
|
||||
pub(crate) use avalue::AValue;
|
||||
pub(crate) use avalue::ResolvedCallResult;
|
||||
|
||||
use crate::execution::ExecutionError;
|
||||
use crate::SecurityTetraplet;
|
||||
use error_descriptor::LastErrorDescriptor;
|
||||
use error_descriptor::LastErrorWithTetraplets;
|
||||
use super::InstrTracker;
|
||||
use super::LastErrorDescriptor;
|
||||
use super::LastErrorWithTetraplet;
|
||||
use crate::execution_step::boxed_value::Scalar;
|
||||
use crate::execution_step::boxed_value::Stream;
|
||||
|
||||
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 set variables.
|
||||
/// Contains all scalars.
|
||||
// TODO: use shared string (Rc<String>) to avoid copying.
|
||||
pub data_cache: HashMap<String, AValue<'i>>,
|
||||
pub scalars: HashMap<String, Scalar<'i>>,
|
||||
|
||||
/// Contains all streams.
|
||||
// TODO: use shared string (Rc<String>) to avoid copying.
|
||||
pub streams: HashMap<String, RefCell<Stream>>,
|
||||
|
||||
/// Set of peer public keys that should receive resulted data.
|
||||
pub next_peer_pks: Vec<String>,
|
||||
|
||||
/// PeerId of a peer executing this AIR script at the moment.
|
||||
pub current_peer_id: String,
|
||||
pub current_peer_id: Rc<String>,
|
||||
|
||||
/// PeerId of a peer send this AIR script.
|
||||
pub init_peer_id: String,
|
||||
@ -55,17 +56,22 @@ pub(crate) struct ExecutionCtx<'i> {
|
||||
/// Indicates that previous executed subtree is complete.
|
||||
/// A subtree treats as a complete if all subtree elements satisfy the following rules:
|
||||
/// - at least one of par subtrees is completed
|
||||
/// - at least one of xor substree is completed without an error
|
||||
/// - 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>,
|
||||
|
||||
/// Tracker of all met instructions.
|
||||
pub tracker: InstrTracker,
|
||||
}
|
||||
|
||||
impl<'i> ExecutionCtx<'i> {
|
||||
pub(crate) fn new(current_peer_id: String, init_peer_id: String) -> Self {
|
||||
let current_peer_id = Rc::new(current_peer_id);
|
||||
|
||||
Self {
|
||||
current_peer_id,
|
||||
init_peer_id,
|
||||
@ -75,9 +81,9 @@ impl<'i> ExecutionCtx<'i> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn last_error(&self) -> LastErrorWithTetraplets {
|
||||
pub(crate) fn last_error(&self) -> LastErrorWithTetraplet {
|
||||
match &self.last_error {
|
||||
Some(error_descriptor) => LastErrorWithTetraplets::from_error_descriptor(error_descriptor, self),
|
||||
Some(error_descriptor) => LastErrorWithTetraplet::from_error_descriptor(error_descriptor, self),
|
||||
None => <_>::default(),
|
||||
}
|
||||
}
|
||||
@ -89,7 +95,7 @@ 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.data_cache.iter() {
|
||||
for (key, value) in self.scalars.iter() {
|
||||
writeln!(f, " {} => {}", key, value)?;
|
||||
}
|
||||
writeln!(f, "current peer id: {}", self.current_peer_id)?;
|
@ -15,12 +15,14 @@
|
||||
*/
|
||||
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::SecurityTetraplet;
|
||||
use crate::execution_step::ExecutionError;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// This struct is intended to track the last arisen error.
|
||||
@ -29,7 +31,7 @@ pub(crate) struct LastErrorDescriptor {
|
||||
pub(crate) error: Rc<ExecutionError>,
|
||||
pub(crate) instruction: String,
|
||||
pub(crate) peer_id: String,
|
||||
pub(crate) tetraplet: Option<SecurityTetraplet>,
|
||||
pub(crate) tetraplet: Option<RSecurityTetraplet>,
|
||||
}
|
||||
|
||||
/// This type is a serialization target for last error. It means that on the AIR script side
|
||||
@ -48,21 +50,21 @@ pub struct LastError {
|
||||
|
||||
/// Helper struct to return last error with tetraplets from the last_error ExecutionCtx method.
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct LastErrorWithTetraplets {
|
||||
pub(crate) struct LastErrorWithTetraplet {
|
||||
pub(crate) last_error: LastError,
|
||||
pub(crate) tetraplets: Vec<SecurityTetraplet>,
|
||||
pub(crate) tetraplet: RSecurityTetraplet,
|
||||
}
|
||||
|
||||
impl<'s> LastErrorWithTetraplets {
|
||||
impl<'s> LastErrorWithTetraplet {
|
||||
pub(crate) fn from_error_descriptor(descriptor: &LastErrorDescriptor, ctx: &ExecutionCtx<'_>) -> Self {
|
||||
let last_error = descriptor.serialize();
|
||||
let tetraplets = descriptor
|
||||
.tetraplet
|
||||
.clone()
|
||||
.unwrap_or_else(|| SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.clone()));
|
||||
let tetraplets = vec![tetraplets];
|
||||
let tetraplet = descriptor.tetraplet.clone().unwrap_or_else(|| {
|
||||
Rc::new(RefCell::new(SecurityTetraplet::literal_tetraplet(
|
||||
ctx.init_peer_id.clone(),
|
||||
)))
|
||||
});
|
||||
|
||||
Self { last_error, tetraplets }
|
||||
Self { last_error, tetraplet }
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,7 +73,7 @@ impl LastErrorDescriptor {
|
||||
error: Rc<ExecutionError>,
|
||||
instruction: String,
|
||||
peer_id: String,
|
||||
tetraplet: Option<SecurityTetraplet>,
|
||||
tetraplet: Option<RSecurityTetraplet>,
|
||||
) -> Self {
|
||||
Self {
|
||||
error,
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#[derive(Default)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct InstrTracker {
|
||||
pub(crate) ap: ApTracker,
|
||||
pub(crate) call: CallTracker,
|
||||
pub(crate) fold: FoldTracker,
|
||||
pub(crate) match_count: usize,
|
||||
pub(crate) mismatch_count: usize,
|
||||
pub(crate) next_count: usize,
|
||||
pub(crate) null_count: usize,
|
||||
pub(crate) par: ParTracker,
|
||||
pub(crate) seq_count: usize,
|
||||
pub(crate) xor_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct ApTracker {
|
||||
pub(crate) seen_count: usize,
|
||||
pub(crate) executed_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct CallTracker {
|
||||
pub(crate) seen_count: usize,
|
||||
pub(crate) executed_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct FoldTracker {
|
||||
pub(crate) seen_scalar_count: usize,
|
||||
pub(crate) seen_stream_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct ParTracker {
|
||||
pub(crate) seen_count: usize,
|
||||
pub(crate) executed_count: usize,
|
||||
}
|
||||
|
||||
impl InstrTracker {
|
||||
pub(crate) fn met_call(&mut self) {
|
||||
self.call.seen_count += 1;
|
||||
}
|
||||
|
||||
pub(crate) fn met_executed_call(&mut self) {
|
||||
self.call.executed_count += 1;
|
||||
}
|
||||
|
||||
pub(crate) fn met_fold_stream(&mut self) {
|
||||
self.fold.seen_stream_count += 1;
|
||||
}
|
||||
}
|
25
air/src/execution_step/execution_context/mod.rs
Normal file
25
air/src/execution_step/execution_context/mod.rs
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
mod context;
|
||||
mod error_descriptor;
|
||||
mod instructions_tracker;
|
||||
|
||||
pub(crate) use context::ExecutionCtx;
|
||||
pub use error_descriptor::LastError;
|
||||
pub(crate) use error_descriptor::LastErrorDescriptor;
|
||||
pub(crate) use error_descriptor::LastErrorWithTetraplet;
|
||||
pub(crate) use instructions_tracker::*;
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 Fluence Labs Limited
|
||||
* 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.
|
||||
@ -22,6 +22,6 @@
|
||||
///
|
||||
/// At the moment, this trait's applied only to errors.
|
||||
pub(crate) trait Joinable {
|
||||
/// Return true, if supplied object is joinable
|
||||
/// Return true, if supplied object is joinable.
|
||||
fn is_joinable(&self) -> bool;
|
||||
}
|
@ -17,18 +17,31 @@
|
||||
mod air;
|
||||
mod boxed_value;
|
||||
mod errors;
|
||||
pub(crate) mod execution_context;
|
||||
mod joinable;
|
||||
mod trace_handler;
|
||||
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::Stream;
|
||||
pub(crate) use errors::Catchable;
|
||||
pub(super) use errors::ExecutionError;
|
||||
pub(self) use joinable::Joinable;
|
||||
pub(crate) use execution_context::ExecutionCtx;
|
||||
use joinable::Joinable;
|
||||
pub(crate) use trace_handler::TraceHandler;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(self) type ExecutionResult<T> = std::result::Result<T, Rc<ExecutionError>>;
|
||||
pub(self) use air_parser::ast::Variable;
|
||||
type ExecutionResult<T> = std::result::Result<T, Rc<ExecutionError>>;
|
||||
type RSecurityTetraplet = Rc<RefCell<crate::SecurityTetraplet>>;
|
||||
type SecurityTetraplets = Vec<RSecurityTetraplet>;
|
||||
|
||||
use air_parser::ast::AstVariable;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! exec_err {
|
44
air/src/execution_step/trace_handler/data_keeper/errors.rs
Normal file
44
air/src/execution_step/trace_handler/data_keeper/errors.rs
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 thiserror::Error as ThisError;
|
||||
|
||||
/// Errors arose out while accessing various interpreter data.
|
||||
#[derive(ThisError, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum KeeperError {
|
||||
/// Errors occurred when trace_len - trace_position < requested_subtrace_len.
|
||||
#[error(
|
||||
"executed trace has {trace_len} elements and current position is {trace_position},\
|
||||
but tried to set {requested_subtrace_len} subtrace_len"
|
||||
)]
|
||||
SetSubtraceLenFailed {
|
||||
requested_subtrace_len: usize,
|
||||
trace_position: usize,
|
||||
trace_len: usize,
|
||||
},
|
||||
|
||||
/// Errors occurred when
|
||||
/// requested_subtrace_len != 0 && requested_pos + requested_subtrace_len > trace_len.
|
||||
#[error(
|
||||
"executed trace has {trace_len} elements,\
|
||||
but tried to set {requested_subtrace_len} subtrace_len and {requested_pos} position"
|
||||
)]
|
||||
SetSubtraceLenAndPosFailed {
|
||||
requested_pos: usize,
|
||||
requested_subtrace_len: usize,
|
||||
trace_len: usize,
|
||||
},
|
||||
}
|
72
air/src/execution_step/trace_handler/data_keeper/keeper.rs
Normal file
72
air/src/execution_step/trace_handler/data_keeper/keeper.rs
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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::ExecutionTrace;
|
||||
use super::MergeCtx;
|
||||
use super::TraceSlider;
|
||||
|
||||
use air_interpreter_data::InterpreterData;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Keeps all necessary data for merging.
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub(crate) struct DataKeeper {
|
||||
pub(crate) prev_ctx: MergeCtx,
|
||||
pub(crate) current_ctx: MergeCtx,
|
||||
pub(crate) new_to_old_pos: HashMap<usize, DataPositions>,
|
||||
pub(crate) result_trace: ExecutionTrace,
|
||||
}
|
||||
|
||||
impl DataKeeper {
|
||||
pub(crate) fn from_data(prev_data: InterpreterData, current_data: InterpreterData) -> Self {
|
||||
let prev_ctx = MergeCtx::from_data(prev_data);
|
||||
let current_ctx = MergeCtx::from_data(current_data);
|
||||
|
||||
Self {
|
||||
prev_ctx,
|
||||
current_ctx,
|
||||
new_to_old_pos: <_>::default(),
|
||||
result_trace: <_>::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn result_states_count(&self) -> usize {
|
||||
self.result_trace.len()
|
||||
}
|
||||
|
||||
pub(crate) fn prev_slider(&self) -> &TraceSlider {
|
||||
&self.prev_ctx.slider
|
||||
}
|
||||
|
||||
pub(crate) fn prev_slider_mut(&mut self) -> &mut TraceSlider {
|
||||
&mut self.prev_ctx.slider
|
||||
}
|
||||
|
||||
pub(crate) fn current_slider(&self) -> &TraceSlider {
|
||||
&self.current_ctx.slider
|
||||
}
|
||||
|
||||
pub(crate) fn current_slider_mut(&mut self) -> &mut TraceSlider {
|
||||
&mut self.current_ctx.slider
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct DataPositions {
|
||||
pub(crate) prev_pos: Option<usize>,
|
||||
pub(crate) current_pos: Option<usize>,
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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::ExecutionTrace;
|
||||
use super::TraceSlider;
|
||||
|
||||
use air_interpreter_data::InterpreterData;
|
||||
use air_interpreter_data::StreamGenerations;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Contains all necessary information about data.
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub(crate) struct MergeCtx {
|
||||
pub(crate) slider: TraceSlider,
|
||||
pub(crate) streams: StreamGenerations,
|
||||
/// This value is used to track the whole trace that each fold is described.
|
||||
/// total_subtrace_len and subtrace_len from a slider are changed in the following way:
|
||||
/// fold:
|
||||
/// start: total = fold_states_count, subtrace_len = len of the first iteration
|
||||
/// i iteration_end: total -= iteration_i len, subtrace_len = len of the i+1 iteration
|
||||
/// end: total = 0
|
||||
/// par => total -= [left, right], new_subtrace_len = total - [left, right], pos += [left, right]
|
||||
total_subtrace_len: usize,
|
||||
}
|
||||
|
||||
impl MergeCtx {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_trace(trace: ExecutionTrace) -> Self {
|
||||
let total_subtrace_len = trace.len();
|
||||
let slider = TraceSlider::new(trace);
|
||||
|
||||
Self {
|
||||
slider,
|
||||
streams: HashMap::new(),
|
||||
total_subtrace_len,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_data(data: InterpreterData) -> Self {
|
||||
let total_subtrace_len = data.trace.len();
|
||||
let slider = TraceSlider::new(data.trace);
|
||||
|
||||
Self {
|
||||
slider,
|
||||
streams: data.streams,
|
||||
total_subtrace_len,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn stream_generation(&self, stream_name: &str) -> Option<u32> {
|
||||
self.streams.get(stream_name).copied()
|
||||
}
|
||||
|
||||
pub(crate) fn set_total_subtrace_len(&mut self, total_subtrace_len: usize) {
|
||||
if total_subtrace_len == 0 {
|
||||
// setting empty subtrace_len is always possible
|
||||
let _ = self.slider.set_subtrace_len(0);
|
||||
}
|
||||
|
||||
self.total_subtrace_len = total_subtrace_len;
|
||||
}
|
||||
|
||||
pub(crate) fn total_subtrace_len(&self) -> usize {
|
||||
if self.total_subtrace_len < self.slider.seen_elements() {
|
||||
return self.slider.subtrace_len();
|
||||
}
|
||||
self.total_subtrace_len - self.slider.seen_elements()
|
||||
}
|
||||
}
|
31
air/src/execution_step/trace_handler/data_keeper/mod.rs
Normal file
31
air/src/execution_step/trace_handler/data_keeper/mod.rs
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod errors;
|
||||
mod keeper;
|
||||
mod merge_ctx;
|
||||
mod trace_slider;
|
||||
|
||||
pub(crate) use errors::KeeperError;
|
||||
pub(crate) use keeper::DataKeeper;
|
||||
pub(crate) use keeper::DataPositions;
|
||||
pub(super) use merge_ctx::MergeCtx;
|
||||
pub(super) use trace_slider::TraceSlider;
|
||||
|
||||
pub(self) type KeeperResult<T> = std::result::Result<T, KeeperError>;
|
||||
|
||||
use super::ExecutedState;
|
||||
use super::ExecutionTrace;
|
109
air/src/execution_step/trace_handler/data_keeper/trace_slider.rs
Normal file
109
air/src/execution_step/trace_handler/data_keeper/trace_slider.rs
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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::ExecutedState;
|
||||
use super::ExecutionTrace;
|
||||
use super::KeeperError::*;
|
||||
use super::KeeperResult;
|
||||
|
||||
/// This slider is intended to slide on a subtrace inside provided trace. This subtrace
|
||||
/// is identified by position and len.
|
||||
// TODO: check for overflow
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct TraceSlider {
|
||||
/// Trace that slider slide on.
|
||||
trace: ExecutionTrace,
|
||||
|
||||
/// Position of current subtrace inside trace.
|
||||
position: usize,
|
||||
|
||||
/// Length of a current subtrace.
|
||||
subtrace_len: usize,
|
||||
|
||||
/// Count of seen elements since the last position update.
|
||||
seen_elements: usize,
|
||||
}
|
||||
|
||||
impl TraceSlider {
|
||||
pub(super) fn new(trace: ExecutionTrace) -> Self {
|
||||
let subtrace_len = trace.len();
|
||||
|
||||
Self {
|
||||
trace,
|
||||
subtrace_len,
|
||||
..<_>::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the next state if current interval length hasn't been reached
|
||||
/// and None otherwise.
|
||||
#[allow(clippy::suspicious_operation_groupings)]
|
||||
pub(crate) fn next_state(&mut self) -> Option<ExecutedState> {
|
||||
if self.seen_elements >= self.subtrace_len || self.position >= self.trace.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let result = self.trace[self.position].clone();
|
||||
self.position += 1;
|
||||
self.seen_elements += 1;
|
||||
Some(result)
|
||||
}
|
||||
|
||||
pub(crate) fn set_position_and_len(&mut self, position: usize, subtrace_len: usize) -> KeeperResult<()> {
|
||||
// it's possible to set empty subtrace_len and inconsistent position
|
||||
if subtrace_len != 0 && position + subtrace_len > self.trace.len() {
|
||||
return Err(SetSubtraceLenAndPosFailed {
|
||||
requested_pos: position,
|
||||
requested_subtrace_len: subtrace_len,
|
||||
trace_len: self.trace.len(),
|
||||
});
|
||||
}
|
||||
|
||||
self.position = position;
|
||||
self.subtrace_len = subtrace_len;
|
||||
self.seen_elements = 0;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn set_subtrace_len(&mut self, subtrace_len: usize) -> KeeperResult<()> {
|
||||
let trace_remainder = self.trace.len() - self.position;
|
||||
if trace_remainder < subtrace_len {
|
||||
return Err(SetSubtraceLenFailed {
|
||||
requested_subtrace_len: subtrace_len,
|
||||
trace_position: self.position,
|
||||
trace_len: self.trace.len(),
|
||||
});
|
||||
}
|
||||
|
||||
self.seen_elements = 0;
|
||||
self.subtrace_len = subtrace_len;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn position(&self) -> usize {
|
||||
self.position
|
||||
}
|
||||
|
||||
pub(crate) fn subtrace_len(&self) -> usize {
|
||||
self.subtrace_len - self.seen_elements
|
||||
}
|
||||
|
||||
pub(crate) fn seen_elements(&self) -> usize {
|
||||
self.seen_elements
|
||||
}
|
||||
}
|
35
air/src/execution_step/trace_handler/errors.rs
Normal file
35
air/src/execution_step/trace_handler/errors.rs
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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::data_keeper::KeeperError;
|
||||
use super::merger::MergeError;
|
||||
use super::state_automata::StateFSMError;
|
||||
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
/// Errors arose out of merging previous data with a new.
|
||||
#[derive(ThisError, Debug)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub(crate) enum TraceHandlerError {
|
||||
#[error("{0}")]
|
||||
KeeperError(#[from] KeeperError),
|
||||
|
||||
#[error("{0}")]
|
||||
MergeError(#[from] MergeError),
|
||||
|
||||
#[error("{0}")]
|
||||
StateFSMError(#[from] StateFSMError),
|
||||
}
|
170
air/src/execution_step/trace_handler/handler.rs
Normal file
170
air/src/execution_step/trace_handler/handler.rs
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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::*;
|
||||
use crate::log_targets::EXECUTED_STATE_CHANGING;
|
||||
use merger::*;
|
||||
|
||||
use air_interpreter_data::InterpreterData;
|
||||
use air_parser::ast::CallOutputValue;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct TraceHandler {
|
||||
pub(crate) data_keeper: DataKeeper,
|
||||
fsm_keeper: FSMKeeper,
|
||||
}
|
||||
|
||||
impl TraceHandler {
|
||||
pub(crate) fn from_data(prev_data: InterpreterData, current_data: InterpreterData) -> Self {
|
||||
let data_keeper = DataKeeper::from_data(prev_data, current_data);
|
||||
|
||||
Self {
|
||||
data_keeper,
|
||||
fsm_keeper: <_>::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns size of elements inside result trace and intended to provide
|
||||
/// a position of next inserted elements.
|
||||
pub(crate) fn trace_pos(&self) -> usize {
|
||||
self.data_keeper.result_trace.len()
|
||||
}
|
||||
|
||||
pub(crate) fn into_result_trace(self) -> ExecutionTrace {
|
||||
self.data_keeper.result_trace
|
||||
}
|
||||
|
||||
pub(crate) fn as_result_trace(&self) -> &ExecutionTrace {
|
||||
&self.data_keeper.result_trace
|
||||
}
|
||||
|
||||
pub(crate) fn subtree_sizes(&self) -> (usize, usize) {
|
||||
let prev_len = self.data_keeper.prev_slider().subtrace_len();
|
||||
let current_len = self.data_keeper.current_slider().subtrace_len();
|
||||
|
||||
(prev_len, current_len)
|
||||
}
|
||||
}
|
||||
|
||||
impl TraceHandler {
|
||||
/// Should be called at the beginning of a call execution.
|
||||
pub(crate) fn meet_call_start(
|
||||
&mut self,
|
||||
output_value: &CallOutputValue<'_>,
|
||||
) -> TraceHandlerResult<MergerCallResult> {
|
||||
try_merge_next_state_as_call(&mut self.data_keeper, output_value).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Should be called when a call instruction was executed successfully. It adds the supplied
|
||||
/// state to the result trace.
|
||||
pub(crate) fn meet_call_end(&mut self, call_result: CallResult) {
|
||||
log::trace!(
|
||||
target: EXECUTED_STATE_CHANGING,
|
||||
" adding new call executed state {:?}",
|
||||
call_result
|
||||
);
|
||||
self.data_keeper.result_trace.push(ExecutedState::Call(call_result));
|
||||
}
|
||||
}
|
||||
|
||||
impl TraceHandler {
|
||||
pub(crate) fn meet_ap_start(&mut self) -> TraceHandlerResult<MergerApResult> {
|
||||
try_merge_next_state_as_ap(&mut self.data_keeper).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn meet_ap_end(&mut self, ap_result: ApResult) {
|
||||
self.data_keeper.result_trace.push(ExecutedState::Ap(ap_result));
|
||||
}
|
||||
}
|
||||
|
||||
impl TraceHandler {
|
||||
pub(crate) fn meet_par_start(&mut self) -> TraceHandlerResult<()> {
|
||||
let ingredients = merger::try_merge_next_state_as_par(&mut self.data_keeper)?;
|
||||
let par_fsm = ParFSM::from_left_started(ingredients, &mut self.data_keeper)?;
|
||||
self.fsm_keeper.push_par(par_fsm);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn meet_par_subtree_end(&mut self, subtree_type: SubtreeType) -> TraceHandlerResult<()> {
|
||||
match subtree_type {
|
||||
SubtreeType::Left => {
|
||||
let par_fsm = self.fsm_keeper.last_par()?;
|
||||
par_fsm.left_completed(&mut self.data_keeper);
|
||||
}
|
||||
SubtreeType::Right => {
|
||||
let par_fsm = self.fsm_keeper.pop_par()?;
|
||||
par_fsm.right_completed(&mut self.data_keeper);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TraceHandler {
|
||||
pub(crate) fn meet_fold_start(&mut self, fold_id: usize) -> TraceHandlerResult<()> {
|
||||
let ingredients = try_merge_next_state_as_fold(&mut self.data_keeper)?;
|
||||
let fold_fsm = FoldFSM::from_fold_start(ingredients, &mut self.data_keeper)?;
|
||||
self.fsm_keeper.add_fold(fold_id, fold_fsm);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn meet_iteration_start(&mut self, fold_id: usize, value_pos: usize) -> TraceHandlerResult<()> {
|
||||
let fold_fsm = self.fsm_keeper.fold_mut(fold_id)?;
|
||||
fold_fsm.meet_iteration_start(value_pos, &mut self.data_keeper)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn meet_iteration_end(&mut self, fold_id: usize) -> TraceHandlerResult<()> {
|
||||
let fold_fsm = self.fsm_keeper.fold_mut(fold_id)?;
|
||||
fold_fsm.meet_iteration_end(&mut self.data_keeper);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn meet_back_iterator(&mut self, fold_id: usize) -> TraceHandlerResult<()> {
|
||||
let fold_fsm = self.fsm_keeper.fold_mut(fold_id)?;
|
||||
fold_fsm.meet_back_iterator(&mut self.data_keeper)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn meet_generation_end(&mut self, fold_id: usize) -> TraceHandlerResult<()> {
|
||||
let fold_fsm = self.fsm_keeper.fold_mut(fold_id)?;
|
||||
fold_fsm.meet_generation_end(&mut self.data_keeper);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn meet_fold_end(&mut self, fold_id: usize) -> TraceHandlerResult<()> {
|
||||
let fold_fsm = self.fsm_keeper.extract_fold(fold_id)?;
|
||||
fold_fsm.meet_fold_end(&mut self.data_keeper);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn fold_end_with_error(&mut self, fold_id: usize) {
|
||||
let fold_fsm = match self.fsm_keeper.extract_fold(fold_id) {
|
||||
Ok(fold_fsm) => fold_fsm,
|
||||
// just passing here is ok, because error could be produced while fold initialization
|
||||
Err(_) => return,
|
||||
};
|
||||
fold_fsm.fold_end_with_error(&mut self.data_keeper);
|
||||
}
|
||||
}
|
66
air/src/execution_step/trace_handler/merger/ap_merger.rs
Normal file
66
air/src/execution_step/trace_handler/merger/ap_merger.rs
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum MergerApResult {
|
||||
/// There is no corresponding state in a trace for this call.
|
||||
Empty,
|
||||
|
||||
/// There was a state in at least one of the contexts. If there were two states in
|
||||
/// both contexts, they were successfully merged.
|
||||
ApResult { res_generation: Option<u32> },
|
||||
}
|
||||
|
||||
pub(crate) fn try_merge_next_state_as_ap(data_keeper: &mut DataKeeper) -> MergeResult<MergerApResult> {
|
||||
use ExecutedState::Ap;
|
||||
|
||||
let prev_state = data_keeper.prev_slider_mut().next_state();
|
||||
let current_state = data_keeper.current_slider_mut().next_state();
|
||||
|
||||
let ap = match (prev_state, current_state) {
|
||||
(Some(Ap(prev_ap)), _) => prev_ap,
|
||||
// check that current state is Ap, but it's impossible to use it, because prev_data
|
||||
// could not have streams with such generations
|
||||
(None, Some(Ap(_))) => return Ok(MergerApResult::Empty),
|
||||
(None, None) => return Ok(MergerApResult::Empty),
|
||||
(prev_state, current_state) => return Err(MergeError::incompatible_states(prev_state, current_state, "ap")),
|
||||
};
|
||||
|
||||
to_merger_result(ap)
|
||||
}
|
||||
|
||||
macro_rules! to_maybe_generation {
|
||||
($ap_result:ident, $generations:expr, $error_ty:ident) => {
|
||||
match $generations.len() {
|
||||
0 => None,
|
||||
1 => Some($generations[0]),
|
||||
_ => {
|
||||
let ap_error = super::ApResultError::$error_ty($ap_result);
|
||||
return Err(super::MergeError::IncorrectApResult(ap_error));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn to_merger_result(ap_result: ApResult) -> MergeResult<MergerApResult> {
|
||||
let res_generation = to_maybe_generation!(ap_result, &ap_result.res_generations, TooManyDstGenerations);
|
||||
|
||||
let ap_result = MergerApResult::ApResult { res_generation };
|
||||
|
||||
Ok(ap_result)
|
||||
}
|
120
air/src/execution_step/trace_handler/merger/call_merger.rs
Normal file
120
air/src/execution_step/trace_handler/merger/call_merger.rs
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod call_result_constructor;
|
||||
mod utils;
|
||||
|
||||
use super::*;
|
||||
use air_parser::ast::CallOutputValue;
|
||||
use call_result_constructor::*;
|
||||
use utils::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum MergerCallResult {
|
||||
/// There is no corresponding state in a trace for this call.
|
||||
Empty,
|
||||
|
||||
/// There was a state in at least one of the contexts. If there were two states in
|
||||
/// both contexts, they were successfully merged.
|
||||
CallResult { value: CallResult, trace_pos: usize },
|
||||
}
|
||||
|
||||
pub(crate) fn try_merge_next_state_as_call(
|
||||
data_keeper: &mut DataKeeper,
|
||||
output_value: &CallOutputValue<'_>,
|
||||
) -> MergeResult<MergerCallResult> {
|
||||
use ExecutedState::Call;
|
||||
use PrepareScheme::*;
|
||||
|
||||
let prev_state = data_keeper.prev_slider_mut().next_state();
|
||||
let current_state = data_keeper.current_slider_mut().next_state();
|
||||
let value_type = ValueType::from_output_value(output_value);
|
||||
|
||||
let (prev_call, current_call) = match (prev_state, current_state) {
|
||||
(Some(Call(prev_call)), Some(Call(current_call))) => (prev_call, current_call),
|
||||
// this special case is needed to merge stream generation in a right way
|
||||
(None, Some(Call(CallResult::Executed(value)))) => {
|
||||
let call_result = merge_current_executed(value, value_type, data_keeper)?;
|
||||
return Ok(prepare_call_result(call_result, Current, data_keeper));
|
||||
}
|
||||
(None, Some(Call(current_call))) => return Ok(prepare_call_result(current_call, Current, data_keeper)),
|
||||
(Some(Call(prev_call)), None) => return Ok(prepare_call_result(prev_call, Previous, data_keeper)),
|
||||
(None, None) => return Ok(MergerCallResult::Empty),
|
||||
(prev_state, current_state) => return Err(MergeError::incompatible_states(prev_state, current_state, "call")),
|
||||
};
|
||||
|
||||
let merged_call = merge_call_result(prev_call, current_call, value_type, data_keeper)?;
|
||||
let call_result = prepare_call_result(merged_call, Both, data_keeper);
|
||||
try_match_value_type(&call_result, value_type)?;
|
||||
|
||||
Ok(call_result)
|
||||
}
|
||||
|
||||
fn merge_call_result(
|
||||
prev_call: CallResult,
|
||||
current_call: CallResult,
|
||||
value_type: ValueType<'_>,
|
||||
data_keeper: &DataKeeper,
|
||||
) -> MergeResult<CallResult> {
|
||||
use CallResult::*;
|
||||
|
||||
let merged_state = match (prev_call, current_call) {
|
||||
(prev @ CallServiceFailed(..), current @ CallServiceFailed(..)) => {
|
||||
check_equal(&prev, ¤t)?;
|
||||
prev
|
||||
}
|
||||
(RequestSentBy(_), current @ CallServiceFailed(..)) => current,
|
||||
(prev @ CallServiceFailed(..), RequestSentBy(_)) => prev,
|
||||
(prev @ RequestSentBy(_), current @ RequestSentBy(_)) => {
|
||||
check_equal(&prev, ¤t)?;
|
||||
prev
|
||||
}
|
||||
// this special case is needed to merge stream generation in a right way
|
||||
(RequestSentBy(_), Executed(value)) => merge_current_executed(value, value_type, data_keeper)?,
|
||||
(prev @ Executed(..), RequestSentBy(_)) => prev,
|
||||
(Executed(prev_value), Executed(current_value)) => merge_executed(prev_value, current_value)?,
|
||||
(prev_call, current_call) => return Err(CallResultError::incompatible_calls(prev_call, current_call)),
|
||||
};
|
||||
|
||||
Ok(merged_state)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) enum ValueType<'i> {
|
||||
Scalar,
|
||||
Stream(&'i str),
|
||||
}
|
||||
|
||||
impl<'i> ValueType<'i> {
|
||||
pub(self) fn from_output_value(output_value: &'i CallOutputValue<'_>) -> Self {
|
||||
use air_parser::ast::AstVariable;
|
||||
|
||||
match output_value {
|
||||
CallOutputValue::Variable(AstVariable::Stream(stream_name)) => ValueType::Stream(stream_name),
|
||||
_ => ValueType::Scalar,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
impl fmt::Display for ValueType<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ValueType::Scalar => write!(f, "scalar"),
|
||||
ValueType::Stream(stream_name) => write!(f, "${}", stream_name),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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::*;
|
||||
|
||||
pub(super) enum PrepareScheme {
|
||||
Previous,
|
||||
Current,
|
||||
Both,
|
||||
}
|
||||
|
||||
pub(super) fn prepare_call_result(
|
||||
value: CallResult,
|
||||
scheme: PrepareScheme,
|
||||
data_keeper: &mut DataKeeper,
|
||||
) -> MergerCallResult {
|
||||
let prev_pos = match scheme {
|
||||
PrepareScheme::Previous | PrepareScheme::Both => Some(data_keeper.prev_slider().position() - 1),
|
||||
PrepareScheme::Current => None,
|
||||
};
|
||||
|
||||
let current_pos = match scheme {
|
||||
PrepareScheme::Current | PrepareScheme::Both => Some(data_keeper.current_slider().position() - 1),
|
||||
PrepareScheme::Previous => None,
|
||||
};
|
||||
|
||||
let data_positions = DataPositions { prev_pos, current_pos };
|
||||
|
||||
let trace_pos = data_keeper.result_states_count();
|
||||
data_keeper.new_to_old_pos.insert(trace_pos, data_positions);
|
||||
|
||||
MergerCallResult::CallResult { value, trace_pos }
|
||||
}
|
108
air/src/execution_step/trace_handler/merger/call_merger/utils.rs
Normal file
108
air/src/execution_step/trace_handler/merger/call_merger/utils.rs
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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::*;
|
||||
use crate::JValue;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(super) fn merge_executed(prev_value: Value, current_value: Value) -> MergeResult<CallResult> {
|
||||
match (&prev_value, ¤t_value) {
|
||||
(Value::Scalar(_), Value::Scalar(_)) => {
|
||||
are_scalars_equal(&prev_value, ¤t_value)?;
|
||||
Ok(CallResult::Executed(prev_value))
|
||||
}
|
||||
(Value::Stream { value: pr, .. }, Value::Stream { value: cr, .. }) => {
|
||||
are_streams_equal(pr, cr, &prev_value, ¤t_value)?;
|
||||
Ok(CallResult::Executed(prev_value))
|
||||
}
|
||||
_ => Err(CallResultError::not_equal_values(prev_value, current_value)),
|
||||
}
|
||||
}
|
||||
|
||||
fn are_scalars_equal(prev_value: &Value, current_value: &Value) -> MergeResult<()> {
|
||||
if prev_value == current_value {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(CallResultError::not_equal_values(
|
||||
prev_value.clone(),
|
||||
current_value.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn are_streams_equal(
|
||||
prev_result_value: &Rc<JValue>,
|
||||
current_result_value: &Rc<JValue>,
|
||||
prev_value: &Value,
|
||||
current_value: &Value,
|
||||
) -> MergeResult<()> {
|
||||
// values from streams could have different generations and it's ok
|
||||
if prev_result_value == current_result_value {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(CallResultError::not_equal_values(
|
||||
prev_value.clone(),
|
||||
current_value.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Merging of value from only current data to a stream is a something special, because it's
|
||||
/// needed to choose generation not from current data, but a maximum from streams on a current peer.
|
||||
/// Maximum versions are tracked in data in a special field called streams.
|
||||
pub(super) fn merge_current_executed(
|
||||
value: Value,
|
||||
value_type: ValueType<'_>,
|
||||
data_keeper: &DataKeeper,
|
||||
) -> MergeResult<CallResult> {
|
||||
match (value, value_type) {
|
||||
(scalar @ Value::Scalar(_), ValueType::Scalar) => Ok(CallResult::Executed(scalar)),
|
||||
(Value::Stream { value, .. }, ValueType::Stream(stream_name)) => {
|
||||
let generation = data_keeper.prev_ctx.stream_generation(stream_name).unwrap_or_default();
|
||||
let stream = Value::Stream { value, generation };
|
||||
Ok(CallResult::Executed(stream))
|
||||
}
|
||||
(value, value_type) => Err(CallResultError::data_not_match(value, value_type)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_equal(prev_call: &CallResult, current_call: &CallResult) -> MergeResult<()> {
|
||||
if prev_call != current_call {
|
||||
Err(CallResultError::incompatible_calls(
|
||||
prev_call.clone(),
|
||||
current_call.clone(),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn try_match_value_type(merged_call: &MergerCallResult, value_type: ValueType<'_>) -> MergeResult<()> {
|
||||
if let MergerCallResult::CallResult { value, .. } = merged_call {
|
||||
return match (value, value_type) {
|
||||
(CallResult::Executed(value @ Value::Scalar(_)), ValueType::Stream(_)) => {
|
||||
Err(CallResultError::data_not_match(value.clone(), value_type))
|
||||
}
|
||||
(CallResult::Executed(value @ Value::Stream { .. }), ValueType::Scalar) => {
|
||||
Err(CallResultError::data_not_match(value.clone(), value_type))
|
||||
}
|
||||
_ => Ok(()),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
146
air/src/execution_step/trace_handler/merger/errors.rs
Normal file
146
air/src/execution_step/trace_handler/merger/errors.rs
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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::call_merger::ValueType;
|
||||
use super::ApResult;
|
||||
use super::CallResult;
|
||||
use super::ExecutedState;
|
||||
use super::FoldResult;
|
||||
use super::KeeperError;
|
||||
use super::Value;
|
||||
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
/// Errors arose out of merging previous data with a new.
|
||||
#[derive(ThisError, Debug)]
|
||||
pub(crate) enum MergeError {
|
||||
/// Errors occurred when previous and current executed states are incompatible.
|
||||
#[error("previous and current data have incompatible states: '{0:?}' '{1:?}'")]
|
||||
IncompatibleExecutedStates(ExecutedState, ExecutedState),
|
||||
|
||||
/// Merger was expected to see other state that was obtained from one of traces
|
||||
/// (the other state was absent).
|
||||
#[error("state from {1} `{0:?}` is incompatible with expected {2}")]
|
||||
DifferentExecutedStateExpected(ExecutedState, DataType, &'static str),
|
||||
|
||||
#[error("{0:?} contains several subtraces with the same value_pos {1}")]
|
||||
ManyRecordsWithSamePos(FoldResult, usize),
|
||||
|
||||
/// Errors occurred when one of the fold subtrace lore doesn't contain 2 descriptors.
|
||||
#[error("fold contains {0} sublore descriptors, but 2 is expected")]
|
||||
FoldIncorrectSubtracesCount(usize),
|
||||
|
||||
/// Errors bubbled from DataKeeper.
|
||||
#[error("{0}")]
|
||||
KeeperError(#[from] KeeperError),
|
||||
|
||||
#[error("{0}")]
|
||||
IncorrectApResult(#[from] ApResultError),
|
||||
|
||||
#[error("{0}")]
|
||||
IncorrectCallResult(#[from] CallResultError),
|
||||
}
|
||||
|
||||
#[derive(ThisError, Debug)]
|
||||
pub(crate) enum ApResultError {
|
||||
/// Error occurred when Ap results contains more then 1 generation in destination.
|
||||
#[error("{0:?} ap result contains too many generations in destination")]
|
||||
TooManyDstGenerations(ApResult),
|
||||
}
|
||||
|
||||
#[derive(ThisError, Debug)]
|
||||
pub(crate) enum CallResultError {
|
||||
#[error("values in call results are not equal: {prev_value:?} != {current_value:?}")]
|
||||
ValuesNotEqual { prev_value: Value, current_value: Value },
|
||||
|
||||
/// Errors occurred when previous and current call results are incompatible.
|
||||
#[error("previous and current call results are incompatible: '{prev_call:?}' '{current_call:?}'")]
|
||||
IncompatibleCallResults {
|
||||
prev_call: CallResult,
|
||||
current_call: CallResult,
|
||||
},
|
||||
|
||||
#[error("air scripts has the following value type '{air_type}' while data other '{data_value:?}'")]
|
||||
DataNotMatchAIR { air_type: String, data_value: Value },
|
||||
}
|
||||
|
||||
impl MergeError {
|
||||
// shouldn't be called with both Nones
|
||||
pub(crate) fn incompatible_states(
|
||||
prev_state: Option<ExecutedState>,
|
||||
current_state: Option<ExecutedState>,
|
||||
expected_state: &'static str,
|
||||
) -> Self {
|
||||
match (prev_state, current_state) {
|
||||
(Some(prev_state), Some(current_state)) => {
|
||||
MergeError::IncompatibleExecutedStates(prev_state, current_state)
|
||||
}
|
||||
(None, Some(current_state)) => {
|
||||
MergeError::DifferentExecutedStateExpected(current_state, DataType::Current, expected_state)
|
||||
}
|
||||
(Some(prev_state), None) => {
|
||||
MergeError::DifferentExecutedStateExpected(prev_state, DataType::Previous, expected_state)
|
||||
}
|
||||
(None, None) => unreachable!("shouldn't be called with both None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// these impl methods allow construction of MergeError and are used to make code more clean
|
||||
impl CallResultError {
|
||||
pub(crate) fn not_equal_values(prev_value: Value, current_value: Value) -> MergeError {
|
||||
let call_result_error = CallResultError::ValuesNotEqual {
|
||||
prev_value,
|
||||
current_value,
|
||||
};
|
||||
|
||||
MergeError::IncorrectCallResult(call_result_error)
|
||||
}
|
||||
|
||||
pub(crate) fn incompatible_calls(prev_call: CallResult, current_call: CallResult) -> MergeError {
|
||||
let call_result_error = CallResultError::IncompatibleCallResults {
|
||||
prev_call,
|
||||
current_call,
|
||||
};
|
||||
|
||||
MergeError::IncorrectCallResult(call_result_error)
|
||||
}
|
||||
|
||||
pub(crate) fn data_not_match(data_value: Value, air_type: ValueType<'_>) -> MergeError {
|
||||
let air_type = air_type.to_string();
|
||||
|
||||
let call_result_error = CallResultError::DataNotMatchAIR { air_type, data_value };
|
||||
|
||||
MergeError::IncorrectCallResult(call_result_error)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum DataType {
|
||||
Previous,
|
||||
Current,
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for DataType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
DataType::Previous => write!(f, "previous"),
|
||||
DataType::Current => write!(f, "current"),
|
||||
}
|
||||
}
|
||||
}
|
79
air/src/execution_step/trace_handler/merger/fold_merger.rs
Normal file
79
air/src/execution_step/trace_handler/merger/fold_merger.rs
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod utils;
|
||||
|
||||
use super::*;
|
||||
pub(crate) use utils::*;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(crate) struct MergerFoldResult {
|
||||
pub(crate) prev_fold_lore: ResolvedFold,
|
||||
pub(crate) current_fold_lore: ResolvedFold,
|
||||
}
|
||||
|
||||
pub(crate) fn try_merge_next_state_as_fold(data_keeper: &mut DataKeeper) -> MergeResult<MergerFoldResult> {
|
||||
use ExecutedState::Fold;
|
||||
|
||||
let prev_state = data_keeper.prev_slider_mut().next_state();
|
||||
let current_state = data_keeper.current_slider_mut().next_state();
|
||||
|
||||
let fold_result = match (prev_state, current_state) {
|
||||
(Some(Fold(prev_fold)), Some(Fold(current_fold))) => {
|
||||
MergerFoldResult::from_fold_results(&prev_fold, ¤t_fold)
|
||||
}
|
||||
(None, Some(Fold(current_fold))) => MergerFoldResult::from_fold_result(¤t_fold, MergeCtxType::Current),
|
||||
(Some(Fold(prev_fold)), None) => MergerFoldResult::from_fold_result(&prev_fold, MergeCtxType::Previous),
|
||||
(None, None) => return Ok(MergerFoldResult::default()),
|
||||
(prev_state, current_state) => return Err(MergeError::incompatible_states(prev_state, current_state, "fold")),
|
||||
}?;
|
||||
|
||||
Ok(fold_result)
|
||||
}
|
||||
|
||||
impl MergerFoldResult {
|
||||
pub(self) fn from_fold_result(fold: &FoldResult, ctx_type: MergeCtxType) -> MergeResult<Self> {
|
||||
let (prev_fold_lore, current_fold_lore) = match ctx_type {
|
||||
MergeCtxType::Previous => {
|
||||
let fold_lore = resolve_fold_lore(fold)?;
|
||||
(fold_lore, <_>::default())
|
||||
}
|
||||
MergeCtxType::Current => {
|
||||
let fold_lore = resolve_fold_lore(fold)?;
|
||||
(<_>::default(), fold_lore)
|
||||
}
|
||||
};
|
||||
|
||||
let merge_result = Self {
|
||||
prev_fold_lore,
|
||||
current_fold_lore,
|
||||
};
|
||||
|
||||
Ok(merge_result)
|
||||
}
|
||||
|
||||
pub(self) fn from_fold_results(prev_fold: &FoldResult, current_fold: &FoldResult) -> MergeResult<Self> {
|
||||
let prev_fold_lore = resolve_fold_lore(prev_fold)?;
|
||||
let current_fold_lore = resolve_fold_lore(current_fold)?;
|
||||
|
||||
let merge_result = Self {
|
||||
prev_fold_lore,
|
||||
current_fold_lore,
|
||||
};
|
||||
|
||||
Ok(merge_result)
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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::*;
|
||||
use air_interpreter_data::FoldSubTraceLore;
|
||||
use air_interpreter_data::SubTraceDesc;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct ResolvedFold {
|
||||
pub(crate) lore: HashMap<usize, ResolvedSubTraceDescs>,
|
||||
pub(crate) fold_states_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct ResolvedSubTraceDescs {
|
||||
pub(crate) before_subtrace: SubTraceDesc,
|
||||
pub(crate) after_subtrace: SubTraceDesc,
|
||||
}
|
||||
|
||||
pub(super) fn resolve_fold_lore(fold: &FoldResult) -> MergeResult<ResolvedFold> {
|
||||
let mut lore = HashMap::with_capacity(fold.lore.len());
|
||||
let mut fold_states_count = 0usize;
|
||||
|
||||
for subtrace_lore in fold.lore.iter() {
|
||||
check_subtrace_lore(subtrace_lore)?;
|
||||
|
||||
let resolved_descs = ResolvedSubTraceDescs {
|
||||
before_subtrace: subtrace_lore.subtraces_desc[0],
|
||||
after_subtrace: subtrace_lore.subtraces_desc[1],
|
||||
};
|
||||
|
||||
fold_states_count += resolved_descs.len();
|
||||
|
||||
if lore.insert(subtrace_lore.value_pos as usize, resolved_descs).is_some() {
|
||||
return Err(MergeError::ManyRecordsWithSamePos(
|
||||
fold.clone(),
|
||||
subtrace_lore.value_pos as usize,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let resolved_fold_lore = ResolvedFold::new(lore, fold_states_count);
|
||||
Ok(resolved_fold_lore)
|
||||
}
|
||||
|
||||
fn check_subtrace_lore(subtrace_lore: &FoldSubTraceLore) -> MergeResult<()> {
|
||||
// this limitation is due to current constraint on count of next inside one fold,
|
||||
// for more info please see comments in the interpreter-data crate
|
||||
const SUBTRACE_DESC_COUNT: usize = 2;
|
||||
|
||||
if subtrace_lore.subtraces_desc.len() != SUBTRACE_DESC_COUNT {
|
||||
return Err(MergeError::FoldIncorrectSubtracesCount(
|
||||
subtrace_lore.subtraces_desc.len(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl ResolvedFold {
|
||||
pub(crate) fn new(lore: HashMap<usize, ResolvedSubTraceDescs>, fold_states_count: usize) -> Self {
|
||||
Self {
|
||||
lore,
|
||||
fold_states_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResolvedSubTraceDescs {
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.before_subtrace.subtrace_len as usize + self.after_subtrace.subtrace_len as usize
|
||||
}
|
||||
}
|
60
air/src/execution_step/trace_handler/merger/mod.rs
Normal file
60
air/src/execution_step/trace_handler/merger/mod.rs
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod ap_merger;
|
||||
mod call_merger;
|
||||
mod errors;
|
||||
mod fold_merger;
|
||||
mod par_merger;
|
||||
|
||||
pub(super) use ap_merger::try_merge_next_state_as_ap;
|
||||
pub(crate) use ap_merger::MergerApResult;
|
||||
pub(super) use call_merger::try_merge_next_state_as_call;
|
||||
pub(crate) use call_merger::MergerCallResult;
|
||||
pub(crate) use errors::ApResultError;
|
||||
pub(crate) use errors::CallResultError;
|
||||
pub(crate) use errors::MergeError;
|
||||
pub(crate) use fold_merger::try_merge_next_state_as_fold;
|
||||
pub(crate) use fold_merger::MergerFoldResult;
|
||||
pub(crate) use fold_merger::ResolvedFold;
|
||||
pub(crate) use fold_merger::ResolvedSubTraceDescs;
|
||||
pub(crate) use par_merger::try_merge_next_state_as_par;
|
||||
pub(crate) use par_merger::MergerParResult;
|
||||
|
||||
type MergeResult<T> = std::result::Result<T, MergeError>;
|
||||
|
||||
use super::data_keeper::DataPositions;
|
||||
use super::data_keeper::KeeperError;
|
||||
use super::DataKeeper;
|
||||
|
||||
use air_interpreter_data::*;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) enum MergeCtxType {
|
||||
Current,
|
||||
Previous,
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for MergeCtxType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
MergeCtxType::Previous => write!(f, "previous"),
|
||||
MergeCtxType::Current => write!(f, "current"),
|
||||
}
|
||||
}
|
||||
}
|
62
air/src/execution_step/trace_handler/merger/par_merger.rs
Normal file
62
air/src/execution_step/trace_handler/merger/par_merger.rs
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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::*;
|
||||
use ExecutedState::Par;
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub(crate) struct MergerParResult {
|
||||
pub(crate) prev_par: Option<ParResult>,
|
||||
pub(crate) current_par: Option<ParResult>,
|
||||
}
|
||||
|
||||
pub(crate) fn try_merge_next_state_as_par(data_keeper: &mut DataKeeper) -> MergeResult<MergerParResult> {
|
||||
let prev_state = data_keeper.prev_slider_mut().next_state();
|
||||
let current_state = data_keeper.current_slider_mut().next_state();
|
||||
|
||||
let result = match (prev_state, current_state) {
|
||||
(Some(Par(prev_par)), Some(Par(current_par))) => MergerParResult::from_pars(prev_par, current_par),
|
||||
(None, Some(Par(current_par))) => MergerParResult::from_current_par(current_par),
|
||||
(Some(Par(prev_par)), None) => MergerParResult::from_prev_par(prev_par),
|
||||
(None, None) => MergerParResult::default(),
|
||||
(prev_state, current_state) => return Err(MergeError::incompatible_states(prev_state, current_state, "par")),
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
impl MergerParResult {
|
||||
pub(self) fn from_pars(prev_par: ParResult, current_par: ParResult) -> Self {
|
||||
Self {
|
||||
prev_par: Some(prev_par),
|
||||
current_par: Some(current_par),
|
||||
}
|
||||
}
|
||||
|
||||
pub(self) fn from_prev_par(prev_par: ParResult) -> Self {
|
||||
Self {
|
||||
prev_par: Some(prev_par),
|
||||
current_par: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(self) fn from_current_par(current_par: ParResult) -> Self {
|
||||
Self {
|
||||
prev_par: None,
|
||||
current_par: Some(current_par),
|
||||
}
|
||||
}
|
||||
}
|
39
air/src/execution_step/trace_handler/mod.rs
Normal file
39
air/src/execution_step/trace_handler/mod.rs
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod data_keeper;
|
||||
mod errors;
|
||||
mod handler;
|
||||
mod merger;
|
||||
mod state_automata;
|
||||
|
||||
pub(crate) use errors::TraceHandlerError;
|
||||
pub(crate) use handler::TraceHandler;
|
||||
pub(crate) use merger::MergerApResult;
|
||||
pub(crate) use merger::MergerCallResult;
|
||||
pub(crate) use state_automata::SubtreeType;
|
||||
|
||||
pub(crate) type TraceHandlerResult<T> = std::result::Result<T, TraceHandlerError>;
|
||||
|
||||
use air_interpreter_data::*;
|
||||
use data_keeper::DataKeeper;
|
||||
use merger::MergeCtxType;
|
||||
use merger::MergerFoldResult;
|
||||
use merger::ResolvedFold;
|
||||
use merger::ResolvedSubTraceDescs;
|
||||
use state_automata::FSMKeeper;
|
||||
use state_automata::FoldFSM;
|
||||
use state_automata::ParFSM;
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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::KeeperError;
|
||||
use super::ParResult;
|
||||
use crate::execution_step::trace_handler::MergeCtxType;
|
||||
use crate::execution_step::trace_handler::ResolvedFold;
|
||||
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
/// Errors arose out of merging previous data with a new.
|
||||
#[derive(ThisError, Debug)]
|
||||
pub(crate) enum StateFSMError {
|
||||
/// Error occurred while trying to access or pop elements from an empty par queue.
|
||||
#[error("par queue is empty, while par FSM is requested")]
|
||||
ParQueueIsEmpty(),
|
||||
|
||||
/// Errors occurred while trying to access or pop elements from queue,
|
||||
/// which contains element of different type.
|
||||
#[error("fold FSM for fold id {0} wasn't found")]
|
||||
FoldFSMNotFound(usize),
|
||||
|
||||
/// Errors occurred when ParResult.0 + ParResult.1 overflows.
|
||||
#[error("overflow is occurred while calculating the entire len occupied by executed states corresponded to current par: '{0:?}'")]
|
||||
ParLenOverflow(ParResult),
|
||||
|
||||
/// Errors occurred when slider.position() + ParResult.0 + ParResult.1 overflows.
|
||||
#[error("overflow is occurred while calculating the new position of a {2} slider for resolved par {0:?} and current position {1}'")]
|
||||
ParPosOverflow(ParResult, usize, MergeCtxType),
|
||||
|
||||
/// Errors occurred when ParResult.0 + ParResult.1 value is bigger than current subtree size.
|
||||
#[error("underflow is occurred while calculating the new position of a {2} slider for resolved par {0:?} and current subtrace len {1}'")]
|
||||
ParLenUnderflow(ParResult, usize, MergeCtxType),
|
||||
|
||||
/// Errors occurred when {0}.fold_states_count + {1} overflows.
|
||||
#[error("overflow is occurred while calculating the new position of a {2} slider for resolved fold {0:?} and current position {1}'")]
|
||||
FoldPosOverflow(ResolvedFold, usize, MergeCtxType),
|
||||
|
||||
/// Errors occurred when {1} - 1{0}.fold_states_count underflows.
|
||||
#[error("underflow is occurred while calculating the new position of a {2} slider for resolved fold {0:?} and current subtrace len {1}'")]
|
||||
FoldLenUnderflow(ResolvedFold, usize, MergeCtxType),
|
||||
|
||||
/// Errors occurred while trying to set a total_subtrace_len that is less than
|
||||
#[error("trying to set total_subtrace_len {0} that is less then len set before {1} for {2} ctx")]
|
||||
TotalSubtraceLenIsLess(usize, usize, MergeCtxType),
|
||||
|
||||
/// Errors bubbled from DataKeeper.
|
||||
#[error("{0}")]
|
||||
KeeperError(#[from] KeeperError),
|
||||
}
|
164
air/src/execution_step/trace_handler/state_automata/fold_fsm.rs
Normal file
164
air/src/execution_step/trace_handler/state_automata/fold_fsm.rs
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod lore_applier;
|
||||
mod lore_ctor;
|
||||
mod lore_ctor_queue;
|
||||
mod state_handler;
|
||||
|
||||
use super::*;
|
||||
use lore_applier::*;
|
||||
use lore_ctor::*;
|
||||
use lore_ctor_queue::*;
|
||||
use state_handler::CtxStateHandler;
|
||||
|
||||
use air_interpreter_data::FoldLore;
|
||||
|
||||
/// This FSM manages fold and keeps internally queue of lore ctors.
|
||||
/// State transitioning functions must work in the following way:
|
||||
/// meet_fold_start.1 ->
|
||||
/// meet_generation_start.N ->
|
||||
/// meet_next.M ->
|
||||
/// meet_prev.M ->
|
||||
/// meet_generation_end.N ->
|
||||
/// meet_fold_end.1
|
||||
/// where .T means that this function should be called exactly T times.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(crate) struct FoldFSM {
|
||||
prev_fold: ResolvedFold,
|
||||
current_fold: ResolvedFold,
|
||||
state_inserter: StateInserter,
|
||||
ctor_queue: SubTraceLoreCtorQueue,
|
||||
result_lore: FoldLore,
|
||||
state_handler: CtxStateHandler,
|
||||
}
|
||||
|
||||
impl FoldFSM {
|
||||
pub(crate) fn from_fold_start(fold_result: MergerFoldResult, data_keeper: &mut DataKeeper) -> FSMResult<Self> {
|
||||
let state_inserter = StateInserter::from_keeper(data_keeper);
|
||||
let state_updater =
|
||||
CtxStateHandler::prepare(&fold_result.prev_fold_lore, &fold_result.current_fold_lore, data_keeper)?;
|
||||
|
||||
data_keeper
|
||||
.prev_ctx
|
||||
.set_total_subtrace_len(fold_result.prev_fold_lore.fold_states_count);
|
||||
data_keeper
|
||||
.current_ctx
|
||||
.set_total_subtrace_len(fold_result.current_fold_lore.fold_states_count);
|
||||
|
||||
let fold_fsm = Self {
|
||||
prev_fold: fold_result.prev_fold_lore,
|
||||
current_fold: fold_result.current_fold_lore,
|
||||
state_inserter,
|
||||
state_handler: state_updater,
|
||||
..<_>::default()
|
||||
};
|
||||
|
||||
Ok(fold_fsm)
|
||||
}
|
||||
|
||||
pub(crate) fn meet_iteration_start(&mut self, value_pos: usize, data_keeper: &mut DataKeeper) -> FSMResult<()> {
|
||||
let (prev_pos, current_pos) = match data_keeper.new_to_old_pos.get(&value_pos) {
|
||||
Some(DataPositions { prev_pos, current_pos }) => (prev_pos, current_pos),
|
||||
None => return self.prepare(None, None, value_pos, data_keeper),
|
||||
};
|
||||
|
||||
let prev_lore = prev_pos.map(|pos| self.prev_fold.lore.remove(&pos)).flatten();
|
||||
let current_lore = current_pos.map(|pos| self.current_fold.lore.remove(&pos)).flatten();
|
||||
|
||||
self.prepare(prev_lore, current_lore, value_pos, data_keeper)
|
||||
}
|
||||
|
||||
fn prepare(
|
||||
&mut self,
|
||||
prev_lore: Option<ResolvedSubTraceDescs>,
|
||||
current_lore: Option<ResolvedSubTraceDescs>,
|
||||
value_pos: usize,
|
||||
data_keeper: &mut DataKeeper,
|
||||
) -> FSMResult<()> {
|
||||
apply_fold_lore_before(data_keeper, &prev_lore, ¤t_lore)?;
|
||||
|
||||
let ctor = SubTraceLoreCtor::from_before_start(value_pos, data_keeper);
|
||||
self.ctor_queue.add_element(ctor, prev_lore, current_lore);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn meet_iteration_end(&mut self, data_keeper: &mut DataKeeper) {
|
||||
self.ctor_queue.current().ctor.before_end(data_keeper);
|
||||
}
|
||||
|
||||
pub(crate) fn meet_back_iterator(&mut self, data_keeper: &mut DataKeeper) -> FSMResult<()> {
|
||||
let back_traversal_started = self.ctor_queue.back_traversal_started();
|
||||
|
||||
let LoreCtorDesc {
|
||||
ctor,
|
||||
prev_lore,
|
||||
current_lore,
|
||||
} = self.ctor_queue.current();
|
||||
|
||||
if !back_traversal_started {
|
||||
ctor.maybe_before_end(data_keeper);
|
||||
ctor.after_start(data_keeper);
|
||||
apply_fold_lore_after(data_keeper, prev_lore, current_lore)?;
|
||||
self.ctor_queue.start_back_traverse();
|
||||
} else {
|
||||
ctor.after_end(data_keeper);
|
||||
self.ctor_queue.traverse_back();
|
||||
|
||||
let LoreCtorDesc {
|
||||
ctor,
|
||||
prev_lore,
|
||||
current_lore,
|
||||
} = self.ctor_queue.current();
|
||||
|
||||
ctor.after_start(data_keeper);
|
||||
apply_fold_lore_after(data_keeper, prev_lore, current_lore)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn meet_generation_end(&mut self, data_keeper: &mut DataKeeper) {
|
||||
self.ctor_queue.finish(data_keeper);
|
||||
self.ctor_queue.end_back_traverse();
|
||||
|
||||
let fold_lore = self.ctor_queue.transform_to_lore();
|
||||
self.result_lore.extend(fold_lore);
|
||||
}
|
||||
|
||||
pub(crate) fn meet_fold_end(self, data_keeper: &mut DataKeeper) {
|
||||
// TODO: check for prev and current lore emptiness
|
||||
let fold_result = FoldResult { lore: self.result_lore };
|
||||
let state = ExecutedState::Fold(fold_result);
|
||||
self.state_inserter.insert(data_keeper, state);
|
||||
self.state_handler.set_final_states(data_keeper);
|
||||
}
|
||||
|
||||
pub(crate) fn fold_end_with_error(mut self, data_keeper: &mut DataKeeper) {
|
||||
self.meet_generation_end(data_keeper);
|
||||
self.meet_fold_end(data_keeper);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(self) enum ByNextPosition {
|
||||
/// Represents executed states before next.
|
||||
Before,
|
||||
|
||||
/// Represents executed states after next.
|
||||
After,
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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::*;
|
||||
use ByNextPosition::*;
|
||||
use MergeCtxType::*;
|
||||
|
||||
/// Adjusts sliders accordingly to a before fold lore state.
|
||||
pub(super) fn apply_fold_lore_before(
|
||||
data_keeper: &mut DataKeeper,
|
||||
prev_fold_lore: &Option<ResolvedSubTraceDescs>,
|
||||
current_fold_lore: &Option<ResolvedSubTraceDescs>,
|
||||
) -> FSMResult<()> {
|
||||
apply_fold_lore(data_keeper, prev_fold_lore, Previous, Before)?;
|
||||
apply_fold_lore(data_keeper, current_fold_lore, Current, Before)
|
||||
}
|
||||
|
||||
/// Adjusts sliders accordingly to an after fold lore state.
|
||||
pub(super) fn apply_fold_lore_after(
|
||||
data_keeper: &mut DataKeeper,
|
||||
prev_fold_lore: &Option<ResolvedSubTraceDescs>,
|
||||
current_fold_lore: &Option<ResolvedSubTraceDescs>,
|
||||
) -> FSMResult<()> {
|
||||
apply_fold_lore(data_keeper, prev_fold_lore, Previous, After)?;
|
||||
apply_fold_lore(data_keeper, current_fold_lore, Current, After)
|
||||
}
|
||||
|
||||
fn apply_fold_lore(
|
||||
data_keeper: &mut DataKeeper,
|
||||
fold_lore: &Option<ResolvedSubTraceDescs>,
|
||||
ctx_type: MergeCtxType,
|
||||
next_position: ByNextPosition,
|
||||
) -> FSMResult<()> {
|
||||
let fold_lore = match fold_lore {
|
||||
Some(fold_lore) => fold_lore,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let slider = match ctx_type {
|
||||
Previous => data_keeper.prev_slider_mut(),
|
||||
Current => data_keeper.current_slider_mut(),
|
||||
};
|
||||
|
||||
match next_position {
|
||||
Before => {
|
||||
slider.set_position_and_len(
|
||||
fold_lore.before_subtrace.begin_pos as _,
|
||||
fold_lore.before_subtrace.subtrace_len as _,
|
||||
)?;
|
||||
}
|
||||
After => {
|
||||
slider.set_position_and_len(
|
||||
fold_lore.after_subtrace.begin_pos as _,
|
||||
fold_lore.after_subtrace.subtrace_len as _,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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::*;
|
||||
use air_interpreter_data::SubTraceDesc;
|
||||
|
||||
/// This struct is a form of FSM and is intended to construct a fold subtrace lore element.
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
|
||||
pub(super) struct SubTraceLoreCtor {
|
||||
value_pos: usize,
|
||||
before_tracker: PositionsTracker,
|
||||
after_tracker: PositionsTracker,
|
||||
state: CtorState,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
|
||||
struct PositionsTracker {
|
||||
pub(self) start_pos: usize,
|
||||
pub(self) end_pos: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum CtorState {
|
||||
BeforeStarted,
|
||||
BeforeCompleted,
|
||||
AfterStarted,
|
||||
AfterCompleted,
|
||||
}
|
||||
|
||||
impl SubTraceLoreCtor {
|
||||
pub(super) fn from_before_start(value_pos: usize, data_keeper: &DataKeeper) -> Self {
|
||||
let before_tracker = PositionsTracker {
|
||||
start_pos: data_keeper.result_states_count(),
|
||||
end_pos: 0,
|
||||
};
|
||||
|
||||
Self {
|
||||
value_pos,
|
||||
before_tracker,
|
||||
..<_>::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn before_end(&mut self, data_keeper: &DataKeeper) {
|
||||
self.before_tracker.end_pos = data_keeper.result_states_count();
|
||||
self.state.next();
|
||||
}
|
||||
|
||||
pub(super) fn maybe_before_end(&mut self, data_keeper: &DataKeeper) {
|
||||
if !matches!(self.state, CtorState::BeforeStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.before_tracker.end_pos = data_keeper.result_states_count();
|
||||
self.state.next();
|
||||
}
|
||||
|
||||
pub(super) fn after_start(&mut self, data_keeper: &DataKeeper) {
|
||||
self.after_tracker.start_pos = data_keeper.result_states_count();
|
||||
self.state.next();
|
||||
}
|
||||
|
||||
pub(super) fn after_end(&mut self, data_keeper: &DataKeeper) {
|
||||
self.after_tracker.end_pos = data_keeper.result_states_count();
|
||||
self.state.next();
|
||||
}
|
||||
|
||||
pub(super) fn into_subtrace_lore(self) -> FoldSubTraceLore {
|
||||
let before = SubTraceDesc {
|
||||
begin_pos: self.before_tracker.start_pos as _,
|
||||
subtrace_len: self.before_tracker.len() as _,
|
||||
};
|
||||
|
||||
let after = SubTraceDesc {
|
||||
begin_pos: self.after_tracker.start_pos as _,
|
||||
subtrace_len: self.after_tracker.len() as _,
|
||||
};
|
||||
|
||||
FoldSubTraceLore {
|
||||
value_pos: self.value_pos as _,
|
||||
subtraces_desc: vec![before, after],
|
||||
}
|
||||
}
|
||||
|
||||
// this function should be called in a situation of early exit from fold,
|
||||
// for more details see the comment above SubTraceLoreCtorQueue::finish().
|
||||
pub(super) fn finish(&mut self, data_keeper: &DataKeeper) {
|
||||
use CtorState::*;
|
||||
|
||||
match self.state {
|
||||
BeforeStarted => {
|
||||
self.before_end(data_keeper);
|
||||
self.after_start(data_keeper);
|
||||
self.after_end(data_keeper);
|
||||
}
|
||||
BeforeCompleted => {
|
||||
self.after_start(data_keeper);
|
||||
self.after_end(data_keeper);
|
||||
}
|
||||
AfterStarted => {
|
||||
self.after_end(data_keeper);
|
||||
}
|
||||
AfterCompleted => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PositionsTracker {
|
||||
pub(self) fn len(&self) -> usize {
|
||||
self.end_pos - self.start_pos
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CtorState {
|
||||
fn default() -> Self {
|
||||
Self::BeforeStarted
|
||||
}
|
||||
}
|
||||
|
||||
impl CtorState {
|
||||
pub(self) fn next(&mut self) {
|
||||
use CtorState::*;
|
||||
|
||||
let next_state = match self {
|
||||
BeforeStarted => BeforeCompleted,
|
||||
BeforeCompleted => AfterStarted,
|
||||
AfterStarted => AfterCompleted,
|
||||
AfterCompleted => AfterCompleted,
|
||||
};
|
||||
|
||||
*self = next_state;
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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::DataKeeper;
|
||||
use super::FoldLore;
|
||||
use super::ResolvedSubTraceDescs;
|
||||
use super::SubTraceLoreCtor;
|
||||
|
||||
/// This queue emulates behaviour of fold states traversal:
|
||||
/// - at first states are traversal forward until the end of states
|
||||
/// - then states are traversal backward the same times
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(super) struct SubTraceLoreCtorQueue {
|
||||
queue: Vec<LoreCtorDesc>,
|
||||
back_traversal_pos: usize,
|
||||
back_traversal_started: bool,
|
||||
}
|
||||
|
||||
impl SubTraceLoreCtorQueue {
|
||||
pub(super) fn current(&mut self) -> &mut LoreCtorDesc {
|
||||
&mut self.queue[self.back_traversal_pos - 1]
|
||||
}
|
||||
|
||||
pub(super) fn add_element(
|
||||
&mut self,
|
||||
ctor: SubTraceLoreCtor,
|
||||
prev_lore: Option<ResolvedSubTraceDescs>,
|
||||
current_lore: Option<ResolvedSubTraceDescs>,
|
||||
) {
|
||||
let new_element = LoreCtorDesc {
|
||||
ctor,
|
||||
prev_lore,
|
||||
current_lore,
|
||||
};
|
||||
self.queue.push(new_element);
|
||||
self.back_traversal_pos += 1;
|
||||
}
|
||||
|
||||
pub(super) fn traverse_back(&mut self) {
|
||||
self.back_traversal_pos -= 1;
|
||||
}
|
||||
|
||||
pub(super) fn start_back_traverse(&mut self) {
|
||||
self.back_traversal_started = true;
|
||||
}
|
||||
|
||||
pub(super) fn end_back_traverse(&mut self) {
|
||||
self.back_traversal_started = false;
|
||||
}
|
||||
|
||||
pub(super) fn back_traversal_started(&self) -> bool {
|
||||
self.back_traversal_started
|
||||
}
|
||||
|
||||
pub(super) fn transform_to_lore(&mut self) -> FoldLore {
|
||||
self.queue
|
||||
.drain(..)
|
||||
.map(|l| l.ctor.into_subtrace_lore())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
// this function should be called in a case of early exit from fold, f.e.
|
||||
// in last error bubbling or join behaviour in the following situations:
|
||||
// (fold iterable iterator
|
||||
// (seq
|
||||
// (call .. [joined_variable])
|
||||
// (next iterator)
|
||||
// )
|
||||
// )
|
||||
//
|
||||
// In such example next wouldn't be called and correspondingly all pushed to
|
||||
// ctor queue states wouldn't be properly finished. This function serves such
|
||||
// situations, having called from generation_end.
|
||||
pub(super) fn finish(&mut self, data_keeper: &DataKeeper) {
|
||||
// TODO: optimize it
|
||||
for ctor in self.queue.iter_mut() {
|
||||
ctor.ctor.finish(data_keeper);
|
||||
}
|
||||
|
||||
// set this to zero to correspond that all states were "observed" with back traversal
|
||||
self.back_traversal_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) struct LoreCtorDesc {
|
||||
pub(super) ctor: SubTraceLoreCtor,
|
||||
pub(super) prev_lore: Option<ResolvedSubTraceDescs>,
|
||||
pub(super) current_lore: Option<ResolvedSubTraceDescs>,
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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::*;
|
||||
use crate::execution_step::trace_handler::MergeCtxType;
|
||||
use crate::execution_step::trace_handler::ResolvedFold;
|
||||
|
||||
/// This state updater manage to do the same thing as SubTreeStateUpdater in ParFSM,
|
||||
/// for details please see its detailed comment.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(super) struct CtxStateHandler {
|
||||
state_pair: CtxStatesPair,
|
||||
}
|
||||
|
||||
impl CtxStateHandler {
|
||||
pub(super) fn prepare(
|
||||
prev_fold: &ResolvedFold,
|
||||
current_fold: &ResolvedFold,
|
||||
data_keeper: &DataKeeper,
|
||||
) -> FSMResult<Self> {
|
||||
let prev_state = compute_new_state(prev_fold, data_keeper, MergeCtxType::Previous)?;
|
||||
let current_state = compute_new_state(current_fold, data_keeper, MergeCtxType::Current)?;
|
||||
let state_pair = CtxStatesPair::new(prev_state, current_state);
|
||||
|
||||
let updater = Self { state_pair };
|
||||
Ok(updater)
|
||||
}
|
||||
|
||||
pub(super) fn set_final_states(self, data_keeper: &mut DataKeeper) {
|
||||
update_ctx_states(self.state_pair, data_keeper)
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_new_state(fold: &ResolvedFold, data_keeper: &DataKeeper, ctx_type: MergeCtxType) -> FSMResult<CtxState> {
|
||||
let ctx = match ctx_type {
|
||||
MergeCtxType::Previous => &data_keeper.prev_ctx,
|
||||
MergeCtxType::Current => &data_keeper.current_ctx,
|
||||
};
|
||||
|
||||
let current_position = ctx.slider.position();
|
||||
let current_len = ctx.slider.subtrace_len();
|
||||
|
||||
let pos = current_position
|
||||
.checked_add(fold.fold_states_count)
|
||||
.ok_or_else(|| StateFSMError::FoldPosOverflow(fold.clone(), current_position, ctx_type))?;
|
||||
|
||||
let subtrace_len = current_len
|
||||
.checked_sub(fold.fold_states_count)
|
||||
.ok_or_else(|| StateFSMError::FoldLenUnderflow(fold.clone(), current_position, ctx_type))?;
|
||||
|
||||
let total_subtrace_len = ctx.total_subtrace_len() - fold.fold_states_count;
|
||||
|
||||
let state = CtxState::new(pos, subtrace_len, total_subtrace_len);
|
||||
Ok(state)
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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::FSMResult;
|
||||
use super::FoldFSM;
|
||||
use super::ParFSM;
|
||||
use super::StateFSMError;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct FSMKeeper {
|
||||
par_stack: Vec<ParFSM>,
|
||||
fold_map: HashMap<usize, FoldFSM>,
|
||||
}
|
||||
|
||||
impl FSMKeeper {
|
||||
pub(crate) fn push_par(&mut self, par_fsm: ParFSM) {
|
||||
self.par_stack.push(par_fsm);
|
||||
}
|
||||
|
||||
pub(crate) fn add_fold(&mut self, fold_id: usize, fold_fsm: FoldFSM) {
|
||||
self.fold_map.insert(fold_id, fold_fsm);
|
||||
}
|
||||
|
||||
pub(crate) fn last_par(&mut self) -> FSMResult<&mut ParFSM> {
|
||||
self.par_stack.last_mut().ok_or(StateFSMError::ParQueueIsEmpty())
|
||||
}
|
||||
|
||||
pub(crate) fn pop_par(&mut self) -> FSMResult<ParFSM> {
|
||||
self.par_stack.pop().ok_or(StateFSMError::ParQueueIsEmpty())
|
||||
}
|
||||
|
||||
pub(crate) fn fold_mut(&mut self, fold_id: usize) -> FSMResult<&mut FoldFSM> {
|
||||
self.fold_map
|
||||
.get_mut(&fold_id)
|
||||
.ok_or(StateFSMError::FoldFSMNotFound(fold_id))
|
||||
}
|
||||
|
||||
pub(crate) fn extract_fold(&mut self, fold_id: usize) -> FSMResult<FoldFSM> {
|
||||
self.fold_map
|
||||
.remove(&fold_id)
|
||||
.ok_or(StateFSMError::FoldFSMNotFound(fold_id))
|
||||
}
|
||||
}
|
45
air/src/execution_step/trace_handler/state_automata/mod.rs
Normal file
45
air/src/execution_step/trace_handler/state_automata/mod.rs
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod errors;
|
||||
mod fold_fsm;
|
||||
mod fsm_queue;
|
||||
mod par_fsm;
|
||||
mod state_inserter;
|
||||
mod utils;
|
||||
|
||||
pub(crate) use errors::StateFSMError;
|
||||
pub(crate) use par_fsm::SubtreeType;
|
||||
pub(crate) type FSMResult<T> = std::result::Result<T, StateFSMError>;
|
||||
|
||||
pub(super) use fold_fsm::FoldFSM;
|
||||
pub(super) use fsm_queue::FSMKeeper;
|
||||
pub(super) use par_fsm::ParFSM;
|
||||
|
||||
use super::data_keeper::DataPositions;
|
||||
use super::data_keeper::KeeperError;
|
||||
use super::merger::MergerParResult;
|
||||
use super::DataKeeper;
|
||||
use super::ExecutedState;
|
||||
use super::FoldResult;
|
||||
use super::FoldSubTraceLore;
|
||||
use super::MergeCtxType;
|
||||
use super::MergerFoldResult;
|
||||
use super::ParResult;
|
||||
use super::ResolvedFold;
|
||||
use super::ResolvedSubTraceDescs;
|
||||
use state_inserter::StateInserter;
|
||||
use utils::*;
|
105
air/src/execution_step/trace_handler/state_automata/par_fsm.rs
Normal file
105
air/src/execution_step/trace_handler/state_automata/par_fsm.rs
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod par_builder;
|
||||
mod state_handler;
|
||||
|
||||
use super::*;
|
||||
use par_builder::ParBuilder;
|
||||
use state_handler::CtxStateHandler;
|
||||
|
||||
/// Manages a par state, its state transitioning functions must be called in the following way:
|
||||
/// from_left_started
|
||||
/// -> left_completed(_with_error)
|
||||
/// -> right_started
|
||||
/// -> right_completed(_with_error)
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(crate) struct ParFSM {
|
||||
prev_par: ParResult,
|
||||
current_par: ParResult,
|
||||
state_inserter: StateInserter,
|
||||
state_handler: CtxStateHandler,
|
||||
par_builder: ParBuilder,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub(crate) enum SubtreeType {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl ParFSM {
|
||||
pub(crate) fn from_left_started(ingredients: MergerParResult, data_keeper: &mut DataKeeper) -> FSMResult<Self> {
|
||||
// default is a par with empty left and right subtrees
|
||||
let prev_par = ingredients.prev_par.unwrap_or_default();
|
||||
let current_par = ingredients.current_par.unwrap_or_default();
|
||||
|
||||
let state_inserter = StateInserter::from_keeper(data_keeper);
|
||||
let state_updater = CtxStateHandler::prepare(prev_par, current_par, data_keeper)?;
|
||||
let par_builder = ParBuilder::from_keeper(data_keeper, &state_inserter);
|
||||
|
||||
let par_fsm = Self {
|
||||
prev_par,
|
||||
current_par,
|
||||
state_inserter,
|
||||
state_handler: state_updater,
|
||||
par_builder,
|
||||
};
|
||||
|
||||
par_fsm.prepare_sliders(data_keeper, SubtreeType::Left)?;
|
||||
|
||||
Ok(par_fsm)
|
||||
}
|
||||
|
||||
pub(crate) fn left_completed(&mut self, data_keeper: &mut DataKeeper) {
|
||||
self.par_builder.track(data_keeper, SubtreeType::Left);
|
||||
self.state_handler.handle_subtree_end(data_keeper, SubtreeType::Left);
|
||||
|
||||
// all invariants were checked in the ctor
|
||||
let _ = self.prepare_sliders(data_keeper, SubtreeType::Right);
|
||||
}
|
||||
|
||||
pub(crate) fn right_completed(mut self, data_keeper: &mut DataKeeper) {
|
||||
self.par_builder.track(data_keeper, SubtreeType::Right);
|
||||
let state = self.par_builder.build();
|
||||
self.state_inserter.insert(data_keeper, state);
|
||||
|
||||
self.state_handler.handle_subtree_end(data_keeper, SubtreeType::Right);
|
||||
}
|
||||
|
||||
fn prepare_sliders(&self, data_keeper: &mut DataKeeper, subtree_type: SubtreeType) -> FSMResult<()> {
|
||||
let (prev_len, current_len) = match subtree_type {
|
||||
SubtreeType::Left => (self.prev_par.left_size, self.current_par.left_size),
|
||||
SubtreeType::Right => (self.prev_par.right_size, self.current_par.right_size),
|
||||
};
|
||||
|
||||
data_keeper.prev_slider_mut().set_subtrace_len(prev_len as _)?;
|
||||
data_keeper.current_slider_mut().set_subtrace_len(current_len as _)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for SubtreeType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
SubtreeType::Left => write!(f, "left"),
|
||||
SubtreeType::Right => write!(f, "right"),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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::*;
|
||||
|
||||
/// Tracks lens of data_keeper.result_trace to build resulted Par state at the end.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(super) struct ParBuilder {
|
||||
saved_states_count: usize,
|
||||
left_subtree_size: usize,
|
||||
right_subtree_size: usize,
|
||||
}
|
||||
|
||||
impl ParBuilder {
|
||||
// StateInserter here needs to guaranteed that ParBuilder creates after it,
|
||||
// it must be so to right track a left subtree size
|
||||
pub(super) fn from_keeper(data_keeper: &DataKeeper, _: &StateInserter) -> Self {
|
||||
let saved_states_count = data_keeper.result_states_count();
|
||||
|
||||
Self {
|
||||
saved_states_count,
|
||||
left_subtree_size: 0,
|
||||
right_subtree_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn track(&mut self, data_keeper: &DataKeeper, subtree_type: SubtreeType) {
|
||||
let prev_states_count = self.saved_states_count;
|
||||
let states_count = data_keeper.result_states_count();
|
||||
let resulted_states_count = states_count - prev_states_count;
|
||||
|
||||
match subtree_type {
|
||||
SubtreeType::Left => self.left_subtree_size = resulted_states_count,
|
||||
SubtreeType::Right => self.right_subtree_size = resulted_states_count,
|
||||
}
|
||||
self.saved_states_count = data_keeper.result_trace.len();
|
||||
}
|
||||
|
||||
pub(super) fn build(self) -> ExecutedState {
|
||||
// TODO: check that usize could be converted into u32
|
||||
ExecutedState::par(self.left_subtree_size, self.right_subtree_size)
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod new_states_calculation;
|
||||
mod utils;
|
||||
|
||||
use super::*;
|
||||
use crate::execution_step::trace_handler::state_automata::par_fsm::state_handler::utils::compute_par_total_lens;
|
||||
use new_states_calculation::compute_new_states;
|
||||
use utils::prepare_total_lens;
|
||||
|
||||
/// At the end of a Par execution it's needed to update subtrace_len and positions of both sliders.
|
||||
///
|
||||
/// To see why it's really needed, imagine the following trace:
|
||||
/// [par 9, 3]
|
||||
/// [par 3, 5] <- left subtree of [par 9, 3]
|
||||
/// [call rs 1] [call rs 2] [call rs 3] <- left subtree of [par 3, 5]
|
||||
/// [call rs 4] [call rs 5] [call rs 6] [call rs 7] [call rs 8] <- right subtree of [par 3, 5]
|
||||
/// [par 1, 1] <- right subtree of [par 9, 3]
|
||||
/// [call e 9] <- left subtree of [par 1, 1]
|
||||
/// [call e 10] <- right subtree of [par 1, 1]
|
||||
///
|
||||
/// where
|
||||
/// call rs N - request sent state of Nth call
|
||||
/// call e N - executed state of Nth call
|
||||
///
|
||||
/// and the following script:
|
||||
/// (par
|
||||
/// (xor
|
||||
/// (par
|
||||
/// (call 1-3)
|
||||
/// (call 4-8)
|
||||
/// )
|
||||
/// (null) <- here could be any non-fallible set of instructions
|
||||
/// )
|
||||
/// (par
|
||||
/// (call 9)
|
||||
/// (call 10)
|
||||
/// )
|
||||
/// )
|
||||
///
|
||||
/// Suppose that call 5 (corresponds to [call rs 5]) will fail (f.e. call_service returns a service
|
||||
/// error). Since it's wrapped with xor, then right subtree of xor (null) will be executed.
|
||||
/// After that next par will be executed. This par has corresponding state [par 1, 1] in a trace,
|
||||
/// and to allow slider to pop it it's needed to set updated position in a proper way, because
|
||||
/// otherwise [call rs 6] will be returned.
|
||||
///
|
||||
/// This struct manages to save the updated lens and pos and update slider states to prevent
|
||||
/// such situations.
|
||||
///
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub(super) struct CtxStateHandler {
|
||||
left_pair: CtxStatesPair,
|
||||
right_pair: CtxStatesPair,
|
||||
}
|
||||
|
||||
impl CtxStateHandler {
|
||||
pub(super) fn prepare(
|
||||
prev_par: ParResult,
|
||||
current_par: ParResult,
|
||||
data_keeper: &mut DataKeeper,
|
||||
) -> FSMResult<Self> {
|
||||
let left_pair = prepare_left_pair(prev_par, current_par, data_keeper)?;
|
||||
let right_pair = prepare_right_pair(prev_par, current_par, data_keeper)?;
|
||||
|
||||
let handler = Self { left_pair, right_pair };
|
||||
|
||||
Ok(handler)
|
||||
}
|
||||
|
||||
pub(super) fn handle_subtree_end(self, data_keeper: &mut DataKeeper, subtree_type: SubtreeType) {
|
||||
match subtree_type {
|
||||
SubtreeType::Left => update_ctx_states(self.left_pair, data_keeper),
|
||||
SubtreeType::Right => update_ctx_states(self.right_pair, data_keeper),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_left_pair(
|
||||
prev_par: ParResult,
|
||||
current_par: ParResult,
|
||||
data_keeper: &mut DataKeeper,
|
||||
) -> FSMResult<CtxStatesPair> {
|
||||
let (prev_nibble, current_nibble) = compute_new_states(data_keeper, prev_par, current_par, SubtreeType::Left)?;
|
||||
let prev_state = CtxState::from_nibble(prev_nibble, prev_nibble.subtrace_len);
|
||||
let current_state = CtxState::from_nibble(current_nibble, current_nibble.subtrace_len);
|
||||
let pair = CtxStatesPair::new(prev_state, current_state);
|
||||
|
||||
Ok(pair)
|
||||
}
|
||||
|
||||
fn prepare_right_pair(
|
||||
prev_par: ParResult,
|
||||
current_par: ParResult,
|
||||
data_keeper: &mut DataKeeper,
|
||||
) -> FSMResult<CtxStatesPair> {
|
||||
let (prev_par_len, current_par_len) = compute_par_total_lens(prev_par, current_par)?;
|
||||
let (prev_total_len, current_total_len) = prepare_total_lens(prev_par_len, current_par_len, data_keeper)?;
|
||||
|
||||
let prev_pos = data_keeper.prev_slider().position() + prev_par_len;
|
||||
let current_pos = data_keeper.current_slider().position() + current_par_len;
|
||||
|
||||
let prev_state = CtxState::new(prev_pos, prev_total_len, prev_total_len);
|
||||
let current_state = CtxState::new(current_pos, current_total_len, current_total_len);
|
||||
let pair = CtxStatesPair::new(prev_state, current_state);
|
||||
|
||||
Ok(pair)
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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::*;
|
||||
|
||||
pub(super) fn compute_new_states(
|
||||
data_keeper: &DataKeeper,
|
||||
prev_par: ParResult,
|
||||
current_par: ParResult,
|
||||
subtree_type: SubtreeType,
|
||||
) -> FSMResult<(CtxStateNibble, CtxStateNibble)> {
|
||||
let (prev_len, current_len) = match subtree_type {
|
||||
SubtreeType::Left => (prev_par.left_size, current_par.left_size),
|
||||
SubtreeType::Right => (prev_par.right_size, current_par.right_size),
|
||||
};
|
||||
|
||||
let prev_nibble = compute_new_state(data_keeper, prev_len as usize, MergeCtxType::Previous, prev_par)?;
|
||||
let current_nibble = compute_new_state(data_keeper, current_len as usize, MergeCtxType::Current, current_par)?;
|
||||
|
||||
Ok((prev_nibble, current_nibble))
|
||||
}
|
||||
|
||||
fn compute_new_state(
|
||||
data_keeper: &DataKeeper,
|
||||
par_subtree_len: usize,
|
||||
ctx_type: MergeCtxType,
|
||||
par: ParResult,
|
||||
) -> FSMResult<CtxStateNibble> {
|
||||
let slider = match ctx_type {
|
||||
MergeCtxType::Previous => data_keeper.prev_slider(),
|
||||
MergeCtxType::Current => data_keeper.current_slider(),
|
||||
};
|
||||
|
||||
let pos = slider
|
||||
.position()
|
||||
.checked_add(par_subtree_len)
|
||||
.ok_or_else(|| StateFSMError::ParPosOverflow(par, slider.position(), MergeCtxType::Previous))?;
|
||||
|
||||
let subtrace_len = slider
|
||||
.subtrace_len()
|
||||
.checked_sub(par_subtree_len)
|
||||
.ok_or_else(|| StateFSMError::ParLenUnderflow(par, slider.subtrace_len(), MergeCtxType::Current))?;
|
||||
|
||||
let nibble = CtxStateNibble::new(pos, subtrace_len);
|
||||
Ok(nibble)
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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::*;
|
||||
|
||||
pub(super) fn prepare_total_lens(
|
||||
prev_size: usize,
|
||||
current_size: usize,
|
||||
data_keeper: &mut DataKeeper,
|
||||
) -> FSMResult<(usize, usize)> {
|
||||
sizes_suits(prev_size, current_size, data_keeper)?;
|
||||
|
||||
// these lens should be set after end of a par
|
||||
let prev_total_len = data_keeper.prev_ctx.total_subtrace_len() - prev_size;
|
||||
let current_total_len = data_keeper.current_ctx.total_subtrace_len() - current_size;
|
||||
|
||||
data_keeper.prev_ctx.set_total_subtrace_len(prev_size);
|
||||
data_keeper.current_ctx.set_total_subtrace_len(current_size);
|
||||
|
||||
Ok((prev_total_len, current_total_len))
|
||||
}
|
||||
|
||||
pub(super) fn compute_par_total_lens(prev_par: ParResult, current_par: ParResult) -> FSMResult<(usize, usize)> {
|
||||
let prev_par_len = prev_par.size().ok_or(StateFSMError::ParLenOverflow(prev_par))?;
|
||||
let current_par_len = current_par.size().ok_or(StateFSMError::ParLenOverflow(prev_par))?;
|
||||
|
||||
Ok((prev_par_len, current_par_len))
|
||||
}
|
||||
|
||||
fn sizes_suits(prev_par_len: usize, current_par_len: usize, data_keeper: &DataKeeper) -> FSMResult<()> {
|
||||
let prev_total_len = data_keeper.prev_ctx.total_subtrace_len();
|
||||
if prev_par_len > prev_total_len {
|
||||
return Err(StateFSMError::TotalSubtraceLenIsLess(
|
||||
prev_par_len,
|
||||
prev_total_len,
|
||||
MergeCtxType::Previous,
|
||||
));
|
||||
}
|
||||
|
||||
let current_total_len = data_keeper.current_ctx.total_subtrace_len();
|
||||
if current_par_len > current_total_len {
|
||||
return Err(StateFSMError::TotalSubtraceLenIsLess(
|
||||
current_par_len,
|
||||
current_total_len,
|
||||
MergeCtxType::Current,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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::DataKeeper;
|
||||
use super::ExecutedState;
|
||||
|
||||
/// This one is intended to optimize insertion in data to avoid insertion in a middle of it.
|
||||
/// This is achieved by inserting a temporary state, track insert position and insert there
|
||||
/// the final state.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(super) struct StateInserter {
|
||||
position: usize,
|
||||
}
|
||||
|
||||
impl StateInserter {
|
||||
pub(super) fn from_keeper(data_keeper: &mut DataKeeper) -> Self {
|
||||
let position = data_keeper.result_trace.len();
|
||||
// this par is a temporary state
|
||||
data_keeper.result_trace.push(ExecutedState::par(0, 0));
|
||||
|
||||
Self { position }
|
||||
}
|
||||
|
||||
pub(super) fn insert(self, data_keeper: &mut DataKeeper, state: ExecutedState) {
|
||||
data_keeper.result_trace[self.position] = state;
|
||||
}
|
||||
}
|
92
air/src/execution_step/trace_handler/state_automata/utils.rs
Normal file
92
air/src/execution_step/trace_handler/state_automata/utils.rs
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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::DataKeeper;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub(super) struct CtxState {
|
||||
pub(super) pos: usize,
|
||||
pub(super) subtrace_len: usize,
|
||||
pub(super) total_subtrace_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub(super) struct CtxStatesPair {
|
||||
pub(super) prev_state: CtxState,
|
||||
pub(super) current_state: CtxState,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub(crate) struct CtxStateNibble {
|
||||
pub(crate) pos: usize,
|
||||
pub(crate) subtrace_len: usize,
|
||||
}
|
||||
|
||||
impl CtxState {
|
||||
pub(super) fn new(pos: usize, subtrace_len: usize, total_subtrace_len: usize) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
subtrace_len,
|
||||
total_subtrace_len,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn from_nibble(nibble: CtxStateNibble, total_subtrace_len: usize) -> Self {
|
||||
Self {
|
||||
pos: nibble.pos,
|
||||
subtrace_len: nibble.subtrace_len,
|
||||
total_subtrace_len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CtxStatesPair {
|
||||
pub(super) fn new(prev_state: CtxState, current_state: CtxState) -> Self {
|
||||
Self {
|
||||
prev_state,
|
||||
current_state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CtxStateNibble {
|
||||
pub(super) fn new(pos: usize, subtrace_len: usize) -> Self {
|
||||
Self { pos, subtrace_len }
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn update_ctx_states(state_pair: CtxStatesPair, data_keeper: &mut DataKeeper) {
|
||||
// these calls shouldn't produce a error, because sizes become less and
|
||||
// they have been already checked in a state updater ctor. It's important
|
||||
// to make it in a such way, because this function could be called from
|
||||
// error_exit that shouldn't fail.
|
||||
let prev_state = state_pair.prev_state;
|
||||
let current_state = state_pair.current_state;
|
||||
|
||||
let _ = data_keeper
|
||||
.prev_slider_mut()
|
||||
.set_position_and_len(prev_state.pos, prev_state.subtrace_len);
|
||||
data_keeper
|
||||
.prev_ctx
|
||||
.set_total_subtrace_len(prev_state.total_subtrace_len);
|
||||
|
||||
let _ = data_keeper
|
||||
.current_slider_mut()
|
||||
.set_position_and_len(current_state.pos, current_state.subtrace_len);
|
||||
data_keeper
|
||||
.current_ctx
|
||||
.set_total_subtrace_len(current_state.subtrace_len);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user