mirror of
https://github.com/fluencelabs/aquavm
synced 2025-03-15 20:40:50 +00:00
feat(execution-engine): a new :error: runtime attribute according with FLIP-11 [fixes VM-329] (#683)
* feat(execution-engine): a new :error: runtime attribute according with FLIP-11 [fixes VM-329]
This commit is contained in:
parent
2fc1686e19
commit
20afb79e3f
@ -14,8 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use super::ErrorAffectable;
|
||||||
use super::Joinable;
|
use super::Joinable;
|
||||||
use super::LastErrorAffectable;
|
|
||||||
use crate::execution_step::execution_context::errors::StreamMapError;
|
use crate::execution_step::execution_context::errors::StreamMapError;
|
||||||
use crate::execution_step::execution_context::LastErrorObjectError;
|
use crate::execution_step::execution_context::LastErrorObjectError;
|
||||||
use crate::execution_step::lambda_applier::LambdaError;
|
use crate::execution_step::lambda_applier::LambdaError;
|
||||||
@ -40,11 +40,11 @@ pub enum CatchableError {
|
|||||||
LocalServiceError(i32, Rc<String>),
|
LocalServiceError(i32, Rc<String>),
|
||||||
|
|
||||||
/// This error type is produced by a match to notify xor that compared values aren't equal.
|
/// This error type is produced by a match to notify xor that compared values aren't equal.
|
||||||
#[error("match is used without corresponding xor")]
|
#[error("compared values do not match")]
|
||||||
MatchValuesNotEqual,
|
MatchValuesNotEqual,
|
||||||
|
|
||||||
/// This error type is produced by a mismatch to notify xor that compared values aren't equal.
|
/// This error type is produced by a mismatch to notify xor that compared values aren't equal.
|
||||||
#[error("mismatch is used without corresponding xor")]
|
#[error("compared values do not mismatch")]
|
||||||
MismatchValuesEqual,
|
MismatchValuesEqual,
|
||||||
|
|
||||||
/// Variable with such a name wasn't defined during AIR script execution.
|
/// Variable with such a name wasn't defined during AIR script execution.
|
||||||
@ -119,13 +119,17 @@ impl ToErrorCode for CatchableError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LastErrorAffectable for CatchableError {
|
impl ErrorAffectable for CatchableError {
|
||||||
fn affects_last_error(&self) -> bool {
|
fn affects_last_error(&self) -> bool {
|
||||||
!matches!(
|
!matches!(
|
||||||
self,
|
self,
|
||||||
CatchableError::MatchValuesNotEqual | CatchableError::MismatchValuesEqual
|
CatchableError::MatchValuesNotEqual | CatchableError::MismatchValuesEqual
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn affects_error(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! log_join {
|
macro_rules! log_join {
|
||||||
|
@ -14,9 +14,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// This trait is intended to figuring out whether a last error should be set or not.
|
/// This trait controls whether to set %last_error% and :error: or not.
|
||||||
pub(crate) trait LastErrorAffectable {
|
pub(crate) trait ErrorAffectable {
|
||||||
/// Return true, if this error type affects last error
|
/// Return true, if this error type affects last error
|
||||||
/// (meaning that it should be set after occurring such an error).
|
/// (meaning that it should be set after occurring such an error).
|
||||||
fn affects_last_error(&self) -> bool;
|
fn affects_last_error(&self) -> bool;
|
||||||
|
fn affects_error(&self) -> bool;
|
||||||
}
|
}
|
@ -15,8 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::CatchableError;
|
use super::CatchableError;
|
||||||
|
use super::ErrorAffectable;
|
||||||
use super::Joinable;
|
use super::Joinable;
|
||||||
use super::LastErrorAffectable;
|
|
||||||
use super::UncatchableError;
|
use super::UncatchableError;
|
||||||
use crate::ToErrorCode;
|
use crate::ToErrorCode;
|
||||||
|
|
||||||
@ -91,11 +91,18 @@ impl Joinable for ExecutionError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LastErrorAffectable for ExecutionError {
|
impl ErrorAffectable for ExecutionError {
|
||||||
fn affects_last_error(&self) -> bool {
|
fn affects_last_error(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
ExecutionError::Catchable(err) => err.affects_last_error(),
|
ExecutionError::Catchable(err) => err.affects_last_error(),
|
||||||
ExecutionError::Uncatchable(_) => false,
|
ExecutionError::Uncatchable(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn affects_error(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ExecutionError::Catchable(_) => true,
|
||||||
|
ExecutionError::Uncatchable(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
mod catchable_errors;
|
mod catchable_errors;
|
||||||
|
mod error_effectable;
|
||||||
mod execution_errors;
|
mod execution_errors;
|
||||||
mod joinable;
|
mod joinable;
|
||||||
mod last_error_affectable;
|
|
||||||
mod uncatchable_errors;
|
mod uncatchable_errors;
|
||||||
|
|
||||||
pub use catchable_errors::CatchableError;
|
pub use catchable_errors::CatchableError;
|
||||||
pub use execution_errors::ExecutionError;
|
pub use execution_errors::ExecutionError;
|
||||||
pub use uncatchable_errors::UncatchableError;
|
pub use uncatchable_errors::UncatchableError;
|
||||||
|
|
||||||
|
pub(crate) use error_effectable::ErrorAffectable;
|
||||||
pub(crate) use joinable::Joinable;
|
pub(crate) use joinable::Joinable;
|
||||||
pub(crate) use last_error_affectable::LastErrorAffectable;
|
|
||||||
|
|
||||||
use super::Stream;
|
use super::Stream;
|
||||||
|
@ -14,12 +14,16 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use super::ErrorDescriptor;
|
||||||
use super::ExecutionCidState;
|
use super::ExecutionCidState;
|
||||||
use super::LastError;
|
use super::InstructionError;
|
||||||
use super::LastErrorDescriptor;
|
use super::LastErrorDescriptor;
|
||||||
use super::Scalars;
|
use super::Scalars;
|
||||||
use super::StreamMaps;
|
use super::StreamMaps;
|
||||||
use super::Streams;
|
use super::Streams;
|
||||||
|
use crate::execution_step::ErrorAffectable;
|
||||||
|
use crate::execution_step::RcSecurityTetraplet;
|
||||||
|
use crate::ToErrorCode;
|
||||||
|
|
||||||
use air_execution_info_collector::InstructionTracker;
|
use air_execution_info_collector::InstructionTracker;
|
||||||
use air_interpreter_cid::CID;
|
use air_interpreter_cid::CID;
|
||||||
@ -51,9 +55,12 @@ pub(crate) struct ExecutionCtx<'i> {
|
|||||||
pub(crate) run_parameters: RcRunParameters,
|
pub(crate) run_parameters: RcRunParameters,
|
||||||
|
|
||||||
/// Last error produced by local service.
|
/// Last error produced by local service.
|
||||||
/// None means that there weren't any error.
|
/// There is the special not-an-error value means there was no error.
|
||||||
pub(crate) last_error_descriptor: LastErrorDescriptor,
|
pub(crate) last_error_descriptor: LastErrorDescriptor,
|
||||||
|
|
||||||
|
/// Error produced by some instructions, e.g. call, match, fail.
|
||||||
|
pub(crate) error_descriptor: ErrorDescriptor,
|
||||||
|
|
||||||
/// Indicates that previous executed subgraph is complete.
|
/// Indicates that previous executed subgraph is complete.
|
||||||
/// A subgraph treats as a complete if all subgraph elements satisfy the following rules:
|
/// A subgraph treats as a complete if all subgraph elements satisfy the following rules:
|
||||||
/// - at least one of par subgraphs is completed
|
/// - at least one of par subgraphs is completed
|
||||||
@ -116,8 +123,12 @@ impl<'i> ExecutionCtx<'i> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn last_error(&self) -> &LastError {
|
pub(crate) fn last_error(&self) -> &InstructionError {
|
||||||
self.last_error_descriptor.last_error()
|
self.last_error_descriptor.error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn error(&self) -> &InstructionError {
|
||||||
|
self.error_descriptor.error()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn next_call_request_id(&mut self) -> u32 {
|
pub(crate) fn next_call_request_id(&mut self) -> u32 {
|
||||||
@ -150,6 +161,41 @@ impl ExecutionCtx<'_> {
|
|||||||
pub(crate) fn flush_subgraph_completeness(&mut self) {
|
pub(crate) fn flush_subgraph_completeness(&mut self) {
|
||||||
self.subgraph_completeness = true;
|
self.subgraph_completeness = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This routine sets %last_error% and :error:.
|
||||||
|
// Most instructions, except Call, Canon, CanonMapScalar does not set :error:.$.peer_id b/c
|
||||||
|
// it would be a non-deterministic peer_id.
|
||||||
|
pub(crate) fn set_errors(
|
||||||
|
&mut self,
|
||||||
|
error: &(impl ErrorAffectable + ToErrorCode + ToString),
|
||||||
|
instruction: &str,
|
||||||
|
tetraplet: Option<RcSecurityTetraplet>,
|
||||||
|
use_tetraplet_and_log_peer_id: bool,
|
||||||
|
) {
|
||||||
|
let last_error_peer_id = match &tetraplet {
|
||||||
|
// use tetraplet if they set, because an error could be propagated from data
|
||||||
|
// (from CallServiceFailed state) and exec_ctx.run_parameters.current_peer_id won't mean
|
||||||
|
// a peer where the error was occurred
|
||||||
|
Some(tetraplet) if use_tetraplet_and_log_peer_id => Some(tetraplet.peer_pk.as_str()),
|
||||||
|
_ => Some(self.run_parameters.current_peer_id.as_str()),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.last_error_descriptor.try_to_set_last_error_from_exec_error(
|
||||||
|
error,
|
||||||
|
instruction,
|
||||||
|
last_error_peer_id,
|
||||||
|
tetraplet.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let peer_id = if use_tetraplet_and_log_peer_id {
|
||||||
|
last_error_peer_id
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
self.error_descriptor
|
||||||
|
.try_to_set_error_from_exec_error(error, instruction, peer_id, tetraplet.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper struct for ExecCtx construction.
|
/// Helper struct for ExecCtx construction.
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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::no_error;
|
||||||
|
use super::InstructionError;
|
||||||
|
use crate::execution_step::ErrorAffectable;
|
||||||
|
use crate::execution_step::RcSecurityTetraplet;
|
||||||
|
use crate::ToErrorCode;
|
||||||
|
|
||||||
|
pub(crate) struct ErrorDescriptor {
|
||||||
|
error: InstructionError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorDescriptor {
|
||||||
|
pub(crate) fn try_to_set_error_from_exec_error(
|
||||||
|
&mut self,
|
||||||
|
error: &(impl ErrorAffectable + ToErrorCode + ToString),
|
||||||
|
instruction: &str,
|
||||||
|
peer_id_option: Option<&str>,
|
||||||
|
tetraplet: Option<RcSecurityTetraplet>,
|
||||||
|
) {
|
||||||
|
use super::get_instruction_error_from_exec_error;
|
||||||
|
|
||||||
|
if !error.affects_error() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.error = get_instruction_error_from_exec_error(error, instruction, peer_id_option, tetraplet);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn error(&self) -> &InstructionError {
|
||||||
|
&self.error
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clear_error(&mut self) {
|
||||||
|
self.error = no_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ErrorDescriptor {
|
||||||
|
fn default() -> Self {
|
||||||
|
let error = no_error();
|
||||||
|
|
||||||
|
Self { error }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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 air_interpreter_data::Provenance;
|
||||||
|
|
||||||
|
use super::instruction_error_definition::error_from_raw_fields;
|
||||||
|
use super::instruction_error_definition::error_from_raw_fields_w_peerid;
|
||||||
|
use super::InstructionError;
|
||||||
|
use crate::execution_step::ErrorAffectable;
|
||||||
|
use crate::execution_step::RcSecurityTetraplet;
|
||||||
|
use crate::JValue;
|
||||||
|
use crate::ToErrorCode;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub(crate) fn get_instruction_error_from_exec_error(
|
||||||
|
error: &(impl ErrorAffectable + ToErrorCode + ToString),
|
||||||
|
instruction: &str,
|
||||||
|
peer_id_option: Option<&str>,
|
||||||
|
tetraplet: Option<RcSecurityTetraplet>,
|
||||||
|
) -> InstructionError {
|
||||||
|
// it is not a call result, but generated from a limited set of unjoinable errors
|
||||||
|
let provenance = Provenance::literal();
|
||||||
|
|
||||||
|
get_instruction_error_from_ingredients(
|
||||||
|
error.to_error_code(),
|
||||||
|
&error.to_string(),
|
||||||
|
instruction,
|
||||||
|
peer_id_option,
|
||||||
|
tetraplet,
|
||||||
|
provenance,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_instruction_error_from_ingredients(
|
||||||
|
error_code: i64,
|
||||||
|
error_message: &str,
|
||||||
|
instruction: &str,
|
||||||
|
peer_id_option: Option<&str>,
|
||||||
|
tetraplet: Option<RcSecurityTetraplet>,
|
||||||
|
provenance: Provenance,
|
||||||
|
) -> InstructionError {
|
||||||
|
let error_object = match peer_id_option {
|
||||||
|
Some(peer_id) => error_from_raw_fields_w_peerid(error_code, error_message, instruction, peer_id),
|
||||||
|
None => error_from_raw_fields(error_code, error_message, instruction),
|
||||||
|
};
|
||||||
|
get_instruction_error_from_error_object(Rc::new(error_object), tetraplet, provenance)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_instruction_error_from_error_object(
|
||||||
|
error: Rc<JValue>,
|
||||||
|
tetraplet: Option<RcSecurityTetraplet>,
|
||||||
|
provenance: Provenance,
|
||||||
|
) -> InstructionError {
|
||||||
|
InstructionError {
|
||||||
|
error,
|
||||||
|
tetraplet,
|
||||||
|
provenance,
|
||||||
|
}
|
||||||
|
}
|
@ -32,14 +32,13 @@ pub const PEER_ID_FIELD_NAME: &str = "peer_id";
|
|||||||
pub const NO_ERROR_MESSAGE: &str = "";
|
pub const NO_ERROR_MESSAGE: &str = "";
|
||||||
pub const NO_ERROR_ERROR_CODE: i64 = 0;
|
pub const NO_ERROR_ERROR_CODE: i64 = 0;
|
||||||
|
|
||||||
/// This struct is intended to track the last arisen error.
|
/// This struct tracks the last arisen error.
|
||||||
/// LastError is essentially a scalar value with support of lambda expressions.
|
/// :error: and %last_error% are special scalar values that support lenses.
|
||||||
/// The only differences from a scalar are
|
/// There are some differences b/w mentioned errors and an ordinary scalar:
|
||||||
/// - it's accessed by %last_error% literal
|
/// - they are set to not-an-error value by default
|
||||||
/// - if it's unset before the usage, JValue::Null will be used without join behaviour
|
/// - these are global scalars, meaning that fold and new scopes do not apply for it
|
||||||
/// - it's a global scalar, meaning that fold and new scopes doesn't apply for it
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct LastError {
|
pub struct InstructionError {
|
||||||
/// Error object that represents the last occurred error.
|
/// Error object that represents the last occurred error.
|
||||||
pub error: Rc<JValue>,
|
pub error: Rc<JValue>,
|
||||||
|
|
||||||
@ -50,7 +49,12 @@ pub struct LastError {
|
|||||||
pub provenance: Provenance,
|
pub provenance: Provenance,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn error_from_raw_fields(error_code: i64, error_message: &str, instruction: &str, peer_id: &str) -> JValue {
|
pub(crate) fn error_from_raw_fields_w_peerid(
|
||||||
|
error_code: i64,
|
||||||
|
error_message: &str,
|
||||||
|
instruction: &str,
|
||||||
|
peer_id: &str,
|
||||||
|
) -> JValue {
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
ERROR_CODE_FIELD_NAME: error_code,
|
ERROR_CODE_FIELD_NAME: error_code,
|
||||||
MESSAGE_FIELD_NAME: error_message,
|
MESSAGE_FIELD_NAME: error_message,
|
||||||
@ -59,6 +63,14 @@ pub(crate) fn error_from_raw_fields(error_code: i64, error_message: &str, instru
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn error_from_raw_fields(error_code: i64, error_message: &str, instruction: &str) -> JValue {
|
||||||
|
serde_json::json!({
|
||||||
|
ERROR_CODE_FIELD_NAME: error_code,
|
||||||
|
MESSAGE_FIELD_NAME: error_message,
|
||||||
|
INSTRUCTION_FIELD_NAME: instruction,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks that a scalar is a value of an object types that contains at least two fields:
|
/// Checks that a scalar is a value of an object types that contains at least two fields:
|
||||||
/// - error_code
|
/// - error_code
|
||||||
/// - message
|
/// - message
|
||||||
@ -116,16 +128,16 @@ fn ensure_jvalue_is_string(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn no_error_last_error_object() -> JValue {
|
pub fn no_error_object() -> JValue {
|
||||||
json!({
|
json!({
|
||||||
ERROR_CODE_FIELD_NAME: NO_ERROR_ERROR_CODE,
|
ERROR_CODE_FIELD_NAME: NO_ERROR_ERROR_CODE,
|
||||||
MESSAGE_FIELD_NAME: NO_ERROR_MESSAGE,
|
MESSAGE_FIELD_NAME: NO_ERROR_MESSAGE,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn no_error_last_error() -> LastError {
|
pub fn no_error() -> InstructionError {
|
||||||
LastError {
|
InstructionError {
|
||||||
error: Rc::new(no_error_last_error_object()),
|
error: Rc::new(no_error_object()),
|
||||||
tetraplet: None,
|
tetraplet: None,
|
||||||
provenance: Provenance::literal(),
|
provenance: Provenance::literal(),
|
||||||
}
|
}
|
@ -16,10 +16,9 @@
|
|||||||
|
|
||||||
use air_interpreter_data::Provenance;
|
use air_interpreter_data::Provenance;
|
||||||
|
|
||||||
use super::last_error_definition::error_from_raw_fields;
|
use super::no_error;
|
||||||
use super::no_error_last_error;
|
use super::InstructionError;
|
||||||
use super::LastError;
|
use crate::execution_step::ErrorAffectable;
|
||||||
use crate::execution_step::LastErrorAffectable;
|
|
||||||
use crate::execution_step::RcSecurityTetraplet;
|
use crate::execution_step::RcSecurityTetraplet;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
use crate::ToErrorCode;
|
use crate::ToErrorCode;
|
||||||
@ -27,7 +26,7 @@ use crate::ToErrorCode;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub(crate) struct LastErrorDescriptor {
|
pub(crate) struct LastErrorDescriptor {
|
||||||
last_error: LastError,
|
error: InstructionError,
|
||||||
|
|
||||||
/// True, if last error could be set. This flag is used to distinguish
|
/// True, if last error could be set. This flag is used to distinguish
|
||||||
/// whether an error is being bubbled up from the bottom or just encountered.
|
/// whether an error is being bubbled up from the bottom or just encountered.
|
||||||
@ -36,44 +35,23 @@ pub(crate) struct LastErrorDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LastErrorDescriptor {
|
impl LastErrorDescriptor {
|
||||||
pub(crate) fn try_to_set_from_error(
|
pub(crate) fn try_to_set_last_error_from_exec_error(
|
||||||
&mut self,
|
&mut self,
|
||||||
error: &(impl LastErrorAffectable + ToErrorCode + ToString),
|
error: &(impl ErrorAffectable + ToErrorCode + ToString),
|
||||||
instruction: &str,
|
instruction: &str,
|
||||||
peer_id: &str,
|
peer_id_option: Option<&str>,
|
||||||
tetraplet: Option<RcSecurityTetraplet>,
|
tetraplet: Option<RcSecurityTetraplet>,
|
||||||
) -> bool {
|
) {
|
||||||
// this check is optimization to prevent creation of an error object in case if error
|
use super::get_instruction_error_from_exec_error;
|
||||||
// couldn't be set
|
|
||||||
|
// This check is an optimization to prevent creation of an error object in case if error
|
||||||
|
// must not be set.
|
||||||
if !self.error_can_be_set || !error.affects_last_error() {
|
if !self.error_can_be_set || !error.affects_last_error() {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// it is not a call result, but generated from a limited set of unjoinable errors
|
self.error = get_instruction_error_from_exec_error(error, instruction, peer_id_option, tetraplet);
|
||||||
let provenance = Provenance::literal();
|
self.error_can_be_set = false;
|
||||||
|
|
||||||
self.set_from_ingredients(
|
|
||||||
error.to_error_code(),
|
|
||||||
&error.to_string(),
|
|
||||||
instruction,
|
|
||||||
peer_id,
|
|
||||||
tetraplet,
|
|
||||||
provenance,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_from_ingredients(
|
|
||||||
&mut self,
|
|
||||||
error_code: i64,
|
|
||||||
error_message: &str,
|
|
||||||
instruction: &str,
|
|
||||||
peer_id: &str,
|
|
||||||
tetraplet: Option<RcSecurityTetraplet>,
|
|
||||||
provenance: Provenance,
|
|
||||||
) -> bool {
|
|
||||||
let error_object = error_from_raw_fields(error_code, error_message, instruction, peer_id);
|
|
||||||
self.set_from_error_object(Rc::new(error_object), tetraplet, provenance);
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_from_error_object(
|
pub(crate) fn set_from_error_object(
|
||||||
@ -82,16 +60,14 @@ impl LastErrorDescriptor {
|
|||||||
tetraplet: Option<RcSecurityTetraplet>,
|
tetraplet: Option<RcSecurityTetraplet>,
|
||||||
provenance: Provenance,
|
provenance: Provenance,
|
||||||
) {
|
) {
|
||||||
self.last_error = LastError {
|
use super::get_instruction_error_from_error_object;
|
||||||
error,
|
|
||||||
tetraplet,
|
self.error = get_instruction_error_from_error_object(error, tetraplet, provenance);
|
||||||
provenance,
|
|
||||||
};
|
|
||||||
self.error_can_be_set = false;
|
self.error_can_be_set = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn last_error(&self) -> &LastError {
|
pub(crate) fn error(&self) -> &InstructionError {
|
||||||
&self.last_error
|
&self.error
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn meet_xor_right_branch(&mut self) {
|
pub(crate) fn meet_xor_right_branch(&mut self) {
|
||||||
@ -105,10 +81,10 @@ impl LastErrorDescriptor {
|
|||||||
|
|
||||||
impl Default for LastErrorDescriptor {
|
impl Default for LastErrorDescriptor {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let last_error = no_error_last_error();
|
let error = no_error();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
last_error,
|
error,
|
||||||
error_can_be_set: true,
|
error_can_be_set: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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 error_descriptor;
|
||||||
|
mod errors;
|
||||||
|
mod errors_utils;
|
||||||
|
mod instruction_error_definition;
|
||||||
|
mod last_error_descriptor;
|
||||||
|
|
||||||
|
pub use errors::LastErrorObjectError;
|
||||||
|
pub use instruction_error_definition::no_error;
|
||||||
|
pub use instruction_error_definition::no_error_object;
|
||||||
|
pub use instruction_error_definition::InstructionError;
|
||||||
|
pub use instruction_error_definition::ERROR_CODE_FIELD_NAME;
|
||||||
|
pub use instruction_error_definition::INSTRUCTION_FIELD_NAME;
|
||||||
|
pub use instruction_error_definition::MESSAGE_FIELD_NAME;
|
||||||
|
pub use instruction_error_definition::NO_ERROR_ERROR_CODE;
|
||||||
|
pub use instruction_error_definition::NO_ERROR_MESSAGE;
|
||||||
|
|
||||||
|
pub(crate) use error_descriptor::ErrorDescriptor;
|
||||||
|
pub(super) use errors_utils::*;
|
||||||
|
pub(crate) use instruction_error_definition::check_error_object;
|
||||||
|
pub(crate) use instruction_error_definition::error_from_raw_fields_w_peerid;
|
||||||
|
pub(crate) use last_error_descriptor::LastErrorDescriptor;
|
@ -1,33 +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 errors;
|
|
||||||
mod last_error_definition;
|
|
||||||
mod last_error_descriptor;
|
|
||||||
|
|
||||||
pub use errors::LastErrorObjectError;
|
|
||||||
pub use last_error_definition::no_error_last_error;
|
|
||||||
pub use last_error_definition::no_error_last_error_object;
|
|
||||||
pub use last_error_definition::LastError;
|
|
||||||
pub use last_error_definition::ERROR_CODE_FIELD_NAME;
|
|
||||||
pub use last_error_definition::INSTRUCTION_FIELD_NAME;
|
|
||||||
pub use last_error_definition::MESSAGE_FIELD_NAME;
|
|
||||||
pub use last_error_definition::NO_ERROR_ERROR_CODE;
|
|
||||||
pub use last_error_definition::NO_ERROR_MESSAGE;
|
|
||||||
|
|
||||||
pub(crate) use last_error_definition::check_error_object;
|
|
||||||
pub(crate) use last_error_definition::error_from_raw_fields;
|
|
||||||
pub(crate) use last_error_descriptor::LastErrorDescriptor;
|
|
@ -16,12 +16,12 @@
|
|||||||
|
|
||||||
mod cid_state;
|
mod cid_state;
|
||||||
mod context;
|
mod context;
|
||||||
mod last_error;
|
mod instruction_error;
|
||||||
mod scalar_variables;
|
mod scalar_variables;
|
||||||
mod stream_maps_variables;
|
mod stream_maps_variables;
|
||||||
mod streams_variables;
|
mod streams_variables;
|
||||||
|
|
||||||
pub use last_error::*;
|
pub use instruction_error::*;
|
||||||
|
|
||||||
pub use cid_state::ExecutionCidState;
|
pub use cid_state::ExecutionCidState;
|
||||||
pub(crate) use context::*;
|
pub(crate) use context::*;
|
||||||
|
@ -23,8 +23,9 @@ use crate::execution_step::PEEK_ALLOWED_ON_NON_EMPTY;
|
|||||||
use crate::UncatchableError;
|
use crate::UncatchableError;
|
||||||
|
|
||||||
use air_interpreter_data::Provenance;
|
use air_interpreter_data::Provenance;
|
||||||
use air_lambda_parser::LambdaAST;
|
use air_lambda_ast::LambdaAST;
|
||||||
use air_parser::ast;
|
use air_parser::ast;
|
||||||
|
use air_parser::ast::InstructionErrorAST;
|
||||||
|
|
||||||
pub(crate) fn apply_to_arg(
|
pub(crate) fn apply_to_arg(
|
||||||
argument: &ast::ApArgument<'_>,
|
argument: &ast::ApArgument<'_>,
|
||||||
@ -36,6 +37,7 @@ pub(crate) fn apply_to_arg(
|
|||||||
|
|
||||||
let result = match argument {
|
let result = match argument {
|
||||||
InitPeerId => apply_const(exec_ctx.run_parameters.init_peer_id.as_ref(), exec_ctx, trace_ctx),
|
InitPeerId => apply_const(exec_ctx.run_parameters.init_peer_id.as_ref(), exec_ctx, trace_ctx),
|
||||||
|
Error(instruction_error) => apply_error(instruction_error, exec_ctx, trace_ctx),
|
||||||
LastError(error_accessor) => apply_last_error(error_accessor, exec_ctx, trace_ctx),
|
LastError(error_accessor) => apply_last_error(error_accessor, exec_ctx, trace_ctx),
|
||||||
Literal(value) => apply_const(*value, exec_ctx, trace_ctx),
|
Literal(value) => apply_const(*value, exec_ctx, trace_ctx),
|
||||||
Timestamp => apply_const(exec_ctx.run_parameters.timestamp, exec_ctx, trace_ctx),
|
Timestamp => apply_const(exec_ctx.run_parameters.timestamp, exec_ctx, trace_ctx),
|
||||||
@ -68,6 +70,21 @@ fn apply_const(
|
|||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_error<'ctx>(
|
||||||
|
instruction_error: &InstructionErrorAST<'ctx>,
|
||||||
|
exec_ctx: &ExecutionCtx<'ctx>,
|
||||||
|
trace_ctx: &TraceHandler,
|
||||||
|
) -> ExecutionResult<ValueAggregate> {
|
||||||
|
let (value, mut tetraplets, provenance) = instruction_error.resolve(exec_ctx)?;
|
||||||
|
let value = Rc::new(value);
|
||||||
|
// removing is safe because prepare_last_error always returns a vec with one element.
|
||||||
|
let tetraplet = tetraplets.remove(0);
|
||||||
|
let position = trace_ctx.trace_pos().map_err(UncatchableError::from)?;
|
||||||
|
|
||||||
|
let result = ValueAggregate::new(value, tetraplet, position, provenance);
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
fn apply_last_error<'i>(
|
fn apply_last_error<'i>(
|
||||||
error_accessor: &Option<LambdaAST<'i>>,
|
error_accessor: &Option<LambdaAST<'i>>,
|
||||||
exec_ctx: &ExecutionCtx<'i>,
|
exec_ctx: &ExecutionCtx<'i>,
|
||||||
|
@ -41,44 +41,43 @@ impl<'i> super::ExecutableInstruction<'i> for Call<'i> {
|
|||||||
exec_ctx.tracker.meet_call();
|
exec_ctx.tracker.meet_call();
|
||||||
|
|
||||||
let resolved_call = joinable!(ResolvedCall::new(self, exec_ctx), exec_ctx, ())
|
let resolved_call = joinable!(ResolvedCall::new(self, exec_ctx), exec_ctx, ())
|
||||||
.map_err(|e| set_last_error(self, exec_ctx, e, None))?;
|
.map_err(|e| set_errors(self, exec_ctx, e, None))?;
|
||||||
|
|
||||||
let tetraplet = resolved_call.as_tetraplet();
|
let tetraplet = resolved_call.as_tetraplet();
|
||||||
joinable!(resolved_call.execute(self, exec_ctx, trace_ctx), exec_ctx, ())
|
joinable!(resolved_call.execute(self, exec_ctx, trace_ctx), exec_ctx, ())
|
||||||
.map_err(|e| set_last_error(self, exec_ctx, e, Some(tetraplet)))
|
.map_err(|e| set_errors(self, exec_ctx, e, Some(tetraplet)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_last_error<'i>(
|
fn set_errors<'i>(
|
||||||
call: &Call<'i>,
|
call: &Call<'i>,
|
||||||
exec_ctx: &mut ExecutionCtx<'i>,
|
exec_ctx: &mut ExecutionCtx<'i>,
|
||||||
execution_error: ExecutionError,
|
execution_error: ExecutionError,
|
||||||
tetraplet: Option<RcSecurityTetraplet>,
|
tetraplet: Option<RcSecurityTetraplet>,
|
||||||
) -> ExecutionError {
|
) -> ExecutionError {
|
||||||
|
use air_parser::ast::PeerIDErrorLogable;
|
||||||
|
|
||||||
let catchable_error = match execution_error {
|
let catchable_error = match execution_error {
|
||||||
ExecutionError::Catchable(catchable) => catchable,
|
ExecutionError::Catchable(catchable) => catchable,
|
||||||
ExecutionError::Uncatchable(_) => return execution_error,
|
ExecutionError::Uncatchable(_) => return execution_error,
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_peer_id = match &tetraplet {
|
exec_ctx.set_errors(
|
||||||
// use tetraplet if they set, because an error could be propagated from data
|
|
||||||
// (from CallServiceFailed state) and exec_ctx.run_parameters.current_peer_id won't mean
|
|
||||||
// a peer where the error was occurred
|
|
||||||
Some(tetraplet) => tetraplet.peer_pk.clone(),
|
|
||||||
None => exec_ctx.run_parameters.current_peer_id.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
log::debug!(
|
|
||||||
"call failed with an error `{}`, peerId `{}`",
|
|
||||||
catchable_error,
|
|
||||||
current_peer_id
|
|
||||||
);
|
|
||||||
|
|
||||||
let _ = exec_ctx.last_error_descriptor.try_to_set_from_error(
|
|
||||||
catchable_error.as_ref(),
|
catchable_error.as_ref(),
|
||||||
&call.to_string(),
|
&call.to_string(),
|
||||||
¤t_peer_id,
|
tetraplet.clone(),
|
||||||
tetraplet,
|
call.log_errors_with_peer_id(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let peer_id = match &tetraplet {
|
||||||
|
// use tetraplet if it is set, because an error could be propagated from data
|
||||||
|
// (from CallServiceFailed state) and exec_ctx.run_parameters.current_peer_id won't mean
|
||||||
|
// a peer where the error was occurred
|
||||||
|
Some(tetraplet) => tetraplet.peer_pk.as_str(),
|
||||||
|
None => exec_ctx.run_parameters.current_peer_id.as_str(),
|
||||||
|
};
|
||||||
|
|
||||||
|
log::debug!("call failed with an error `{}`, peerId `{}`", catchable_error, peer_id);
|
||||||
|
|
||||||
ExecutionError::Catchable(catchable_error)
|
ExecutionError::Catchable(catchable_error)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ use super::TraceHandler;
|
|||||||
use crate::execution_step::execution_context::check_error_object;
|
use crate::execution_step::execution_context::check_error_object;
|
||||||
use crate::execution_step::resolver::Resolvable;
|
use crate::execution_step::resolver::Resolvable;
|
||||||
use crate::execution_step::CatchableError;
|
use crate::execution_step::CatchableError;
|
||||||
use crate::execution_step::LastError;
|
|
||||||
use crate::execution_step::RcSecurityTetraplet;
|
use crate::execution_step::RcSecurityTetraplet;
|
||||||
use crate::log_instruction;
|
use crate::log_instruction;
|
||||||
use crate::ExecutionError;
|
use crate::ExecutionError;
|
||||||
@ -75,7 +74,7 @@ fn fail_with_literals(
|
|||||||
fail: &Fail<'_>,
|
fail: &Fail<'_>,
|
||||||
exec_ctx: &mut ExecutionCtx<'_>,
|
exec_ctx: &mut ExecutionCtx<'_>,
|
||||||
) -> ExecutionResult<()> {
|
) -> ExecutionResult<()> {
|
||||||
let error_object = crate::execution_step::execution_context::error_from_raw_fields(
|
let error_object = crate::execution_step::execution_context::error_from_raw_fields_w_peerid(
|
||||||
error_code,
|
error_code,
|
||||||
error_message,
|
error_message,
|
||||||
&fail.to_string(),
|
&fail.to_string(),
|
||||||
@ -103,11 +102,13 @@ fn fail_with_canon_stream(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fail_with_last_error(exec_ctx: &mut ExecutionCtx<'_>) -> ExecutionResult<()> {
|
fn fail_with_last_error(exec_ctx: &mut ExecutionCtx<'_>) -> ExecutionResult<()> {
|
||||||
let LastError {
|
use crate::execution_step::InstructionError;
|
||||||
|
|
||||||
|
let InstructionError {
|
||||||
error,
|
error,
|
||||||
tetraplet,
|
tetraplet,
|
||||||
provenance,
|
provenance,
|
||||||
} = exec_ctx.last_error_descriptor.last_error();
|
} = exec_ctx.last_error_descriptor.error();
|
||||||
|
|
||||||
// to avoid warnings from https://github.com/rust-lang/rust/issues/59159
|
// to avoid warnings from https://github.com/rust-lang/rust/issues/59159
|
||||||
let error = error.clone();
|
let error = error.clone();
|
||||||
|
@ -46,21 +46,14 @@ use super::ExecutionResult;
|
|||||||
use crate::execution_step::TraceHandler;
|
use crate::execution_step::TraceHandler;
|
||||||
|
|
||||||
use air_parser::ast::Instruction;
|
use air_parser::ast::Instruction;
|
||||||
|
use air_parser::ast::PeerIDErrorLogable;
|
||||||
|
|
||||||
// TODO: move all error set logic from macros into the execution context
|
/// Executes an instruction and updates %last_error% and :error: if necessary.
|
||||||
|
|
||||||
/// Executes instruction and updates last error if needed.
|
|
||||||
macro_rules! execute {
|
macro_rules! execute {
|
||||||
($self:expr, $instr:expr, $exec_ctx:ident, $trace_ctx:ident) => {{
|
($self:expr, $instr:expr, $exec_ctx:ident, $trace_ctx:ident) => {{
|
||||||
match $instr.execute($exec_ctx, $trace_ctx) {
|
match $instr.execute($exec_ctx, $trace_ctx) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
$exec_ctx.last_error_descriptor.try_to_set_from_error(
|
$exec_ctx.set_errors(&e, &$instr.to_string(), None, $instr.log_errors_with_peer_id());
|
||||||
&e,
|
|
||||||
// TODO: avoid excess copying here
|
|
||||||
&$instr.to_string(),
|
|
||||||
$exec_ctx.run_parameters.current_peer_id.as_ref(),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
Err(e)
|
Err(e)
|
||||||
}
|
}
|
||||||
v => v,
|
v => v,
|
||||||
@ -76,13 +69,14 @@ impl<'i> ExecutableInstruction<'i> for Instruction<'i> {
|
|||||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||||
match self {
|
match self {
|
||||||
// call isn't wrapped by the execute macro because
|
// call isn't wrapped by the execute macro because
|
||||||
// it internally sets last_error with resolved triplet
|
// it internally maps some Catchables into %last_error%/:error: using resolved triplet.
|
||||||
|
// Both canons and call set :error:.$.peer_id whilst other instructions do not.
|
||||||
Instruction::Call(call) => call.execute(exec_ctx, trace_ctx),
|
Instruction::Call(call) => call.execute(exec_ctx, trace_ctx),
|
||||||
|
|
||||||
Instruction::Ap(ap) => execute!(self, ap, exec_ctx, trace_ctx),
|
|
||||||
Instruction::ApMap(ap_map) => execute!(self, ap_map, exec_ctx, trace_ctx),
|
|
||||||
Instruction::Canon(canon) => execute!(self, canon, exec_ctx, trace_ctx),
|
Instruction::Canon(canon) => execute!(self, canon, exec_ctx, trace_ctx),
|
||||||
Instruction::CanonStreamMapScalar(canon) => execute!(self, canon, exec_ctx, trace_ctx),
|
Instruction::CanonStreamMapScalar(canon) => execute!(self, canon, exec_ctx, trace_ctx),
|
||||||
|
Instruction::Ap(ap) => execute!(self, ap, exec_ctx, trace_ctx),
|
||||||
|
Instruction::ApMap(ap_map) => execute!(self, ap_map, exec_ctx, trace_ctx),
|
||||||
Instruction::Fail(fail) => execute!(self, fail, exec_ctx, trace_ctx),
|
Instruction::Fail(fail) => execute!(self, fail, exec_ctx, trace_ctx),
|
||||||
Instruction::FoldScalar(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
Instruction::FoldScalar(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
||||||
Instruction::FoldStream(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
Instruction::FoldStream(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
||||||
|
@ -33,7 +33,13 @@ impl<'i> super::ExecutableInstruction<'i> for Xor<'i> {
|
|||||||
|
|
||||||
exec_ctx.flush_subgraph_completeness();
|
exec_ctx.flush_subgraph_completeness();
|
||||||
exec_ctx.last_error_descriptor.meet_xor_right_branch();
|
exec_ctx.last_error_descriptor.meet_xor_right_branch();
|
||||||
self.1.execute(exec_ctx, trace_ctx)
|
|
||||||
|
let right_subgraph_result = self.1.execute(exec_ctx, trace_ctx);
|
||||||
|
// This sets :error: to a no-error state.
|
||||||
|
// Please note the right_subgraph_result might be an Error that bubbles up to an :error:
|
||||||
|
// above this execute().
|
||||||
|
exec_ctx.error_descriptor.clear_error();
|
||||||
|
right_subgraph_result
|
||||||
}
|
}
|
||||||
res => res,
|
res => res,
|
||||||
}
|
}
|
||||||
|
@ -40,10 +40,10 @@ pub mod errors_prelude {
|
|||||||
|
|
||||||
pub(super) use self::instructions::ExecutableInstruction;
|
pub(super) use self::instructions::ExecutableInstruction;
|
||||||
pub(super) use self::instructions::FoldState;
|
pub(super) use self::instructions::FoldState;
|
||||||
|
pub(crate) use errors::ErrorAffectable;
|
||||||
pub(crate) use errors::Joinable;
|
pub(crate) use errors::Joinable;
|
||||||
pub(crate) use errors::LastErrorAffectable;
|
|
||||||
pub(crate) use execution_context::ExecutionCtx;
|
pub(crate) use execution_context::ExecutionCtx;
|
||||||
pub(crate) use execution_context::LastError;
|
pub(crate) use execution_context::InstructionError;
|
||||||
pub(super) use value_types::CanonResultAggregate;
|
pub(super) use value_types::CanonResultAggregate;
|
||||||
pub(super) use value_types::Generation;
|
pub(super) use value_types::Generation;
|
||||||
pub(super) use value_types::LiteralAggregate;
|
pub(super) use value_types::LiteralAggregate;
|
||||||
|
@ -21,12 +21,13 @@ use crate::execution_step::lambda_applier::select_by_lambda_from_scalar;
|
|||||||
use crate::execution_step::value_types::JValuable;
|
use crate::execution_step::value_types::JValuable;
|
||||||
use crate::execution_step::ExecutionResult;
|
use crate::execution_step::ExecutionResult;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
use crate::LambdaAST;
|
|
||||||
use crate::SecurityTetraplet;
|
use crate::SecurityTetraplet;
|
||||||
|
|
||||||
use air_interpreter_data::Provenance;
|
use air_interpreter_data::Provenance;
|
||||||
|
use air_lambda_ast::LambdaAST;
|
||||||
use air_parser::ast;
|
use air_parser::ast;
|
||||||
|
|
||||||
|
use air_parser::ast::InstructionErrorAST;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ impl Resolvable for ast::ImmutableValue<'_> {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
InitPeerId => resolve_const(ctx.run_parameters.init_peer_id.as_ref(), ctx),
|
InitPeerId => resolve_const(ctx.run_parameters.init_peer_id.as_ref(), ctx),
|
||||||
|
Error(error_accessor) => error_accessor.resolve(ctx),
|
||||||
LastError(error_accessor) => error_accessor.resolve(ctx),
|
LastError(error_accessor) => error_accessor.resolve(ctx),
|
||||||
Literal(value) => resolve_const(value.to_string(), ctx),
|
Literal(value) => resolve_const(value.to_string(), ctx),
|
||||||
Timestamp => resolve_const(ctx.run_parameters.timestamp, ctx),
|
Timestamp => resolve_const(ctx.run_parameters.timestamp, ctx),
|
||||||
@ -61,31 +63,47 @@ pub(crate) fn resolve_const(
|
|||||||
Ok((jvalue, vec![tetraplet], Provenance::literal()))
|
Ok((jvalue, vec![tetraplet], Provenance::literal()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_errors(
|
||||||
|
instruction_error: &crate::InstructionError,
|
||||||
|
lens: &Option<LambdaAST<'_>>,
|
||||||
|
ctx: &ExecutionCtx<'_>,
|
||||||
|
) -> Result<(serde_json::Value, Vec<Rc<SecurityTetraplet>>, Provenance), crate::ExecutionError> {
|
||||||
|
use crate::execution_step::InstructionError;
|
||||||
|
|
||||||
|
let InstructionError {
|
||||||
|
error,
|
||||||
|
tetraplet,
|
||||||
|
provenance,
|
||||||
|
} = instruction_error;
|
||||||
|
|
||||||
|
let jvalue = match lens {
|
||||||
|
Some(error_accessor) => select_by_lambda_from_scalar(error.as_ref(), error_accessor, ctx)?.into_owned(),
|
||||||
|
None => error.as_ref().clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let tetraplets = match tetraplet {
|
||||||
|
Some(tetraplet) => vec![tetraplet.clone()],
|
||||||
|
None => {
|
||||||
|
let tetraplet = SecurityTetraplet::literal_tetraplet(ctx.run_parameters.init_peer_id.as_ref());
|
||||||
|
let tetraplet = Rc::new(tetraplet);
|
||||||
|
vec![tetraplet]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((jvalue, tetraplets, provenance.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lens> Resolvable for InstructionErrorAST<'lens> {
|
||||||
|
fn resolve(&self, ctx: &ExecutionCtx<'_>) -> ExecutionResult<(JValue, RcSecurityTetraplets, Provenance)> {
|
||||||
|
let instruction_error = ctx.error();
|
||||||
|
resolve_errors(instruction_error, &self.lens, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Resolvable for Option<LambdaAST<'_>> {
|
impl Resolvable for Option<LambdaAST<'_>> {
|
||||||
fn resolve(&self, ctx: &ExecutionCtx<'_>) -> ExecutionResult<(JValue, RcSecurityTetraplets, Provenance)> {
|
fn resolve(&self, ctx: &ExecutionCtx<'_>) -> ExecutionResult<(JValue, RcSecurityTetraplets, Provenance)> {
|
||||||
use crate::LastError;
|
let instruction_error = ctx.last_error();
|
||||||
|
resolve_errors(instruction_error, self, ctx)
|
||||||
let LastError {
|
|
||||||
error,
|
|
||||||
tetraplet,
|
|
||||||
provenance,
|
|
||||||
} = ctx.last_error();
|
|
||||||
|
|
||||||
let jvalue = match self {
|
|
||||||
Some(error_accessor) => select_by_lambda_from_scalar(error.as_ref(), error_accessor, ctx)?.into_owned(),
|
|
||||||
None => error.as_ref().clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let tetraplets = match tetraplet {
|
|
||||||
Some(tetraplet) => vec![tetraplet.clone()],
|
|
||||||
None => {
|
|
||||||
let tetraplet = SecurityTetraplet::literal_tetraplet(ctx.run_parameters.init_peer_id.as_ref());
|
|
||||||
let tetraplet = Rc::new(tetraplet);
|
|
||||||
vec![tetraplet]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((jvalue, tetraplets, provenance.clone()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,10 +37,10 @@ pub use air_interpreter_interface::RunParameters;
|
|||||||
pub use air_interpreter_interface::INTERPRETER_SUCCESS;
|
pub use air_interpreter_interface::INTERPRETER_SUCCESS;
|
||||||
pub use execution_step::execution_context::errors::unsupported_map_key_type;
|
pub use execution_step::execution_context::errors::unsupported_map_key_type;
|
||||||
pub use execution_step::execution_context::errors::StreamMapError;
|
pub use execution_step::execution_context::errors::StreamMapError;
|
||||||
pub use execution_step::execution_context::no_error_last_error;
|
pub use execution_step::execution_context::no_error;
|
||||||
pub use execution_step::execution_context::no_error_last_error_object;
|
pub use execution_step::execution_context::no_error_object;
|
||||||
pub use execution_step::execution_context::ExecutionCidState;
|
pub use execution_step::execution_context::ExecutionCidState;
|
||||||
pub use execution_step::execution_context::LastError;
|
pub use execution_step::execution_context::InstructionError;
|
||||||
pub use execution_step::execution_context::NO_ERROR_ERROR_CODE;
|
pub use execution_step::execution_context::NO_ERROR_ERROR_CODE;
|
||||||
pub use execution_step::execution_context::NO_ERROR_MESSAGE;
|
pub use execution_step::execution_context::NO_ERROR_MESSAGE;
|
||||||
pub use execution_step::CatchableError;
|
pub use execution_step::CatchableError;
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use air::no_error_last_error_object;
|
use air::no_error_object;
|
||||||
use air::CatchableError;
|
use air::CatchableError;
|
||||||
use air::ExecutionCidState;
|
use air::ExecutionCidState;
|
||||||
use air::ExecutionError;
|
use air::ExecutionError;
|
||||||
@ -124,7 +124,7 @@ fn not_clear_last_error_in_match() {
|
|||||||
let _ = checked_call_vm!(local_vm, <_>::default(), &script, "", result.data);
|
let _ = checked_call_vm!(local_vm, <_>::default(), &script, "", result.data);
|
||||||
|
|
||||||
let actual_value = (*args.borrow()).as_ref().unwrap().clone();
|
let actual_value = (*args.borrow()).as_ref().unwrap().clone();
|
||||||
assert_eq!(actual_value, no_error_last_error_object(),);
|
assert_eq!(actual_value, no_error_object(),);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -159,7 +159,7 @@ fn not_clear_last_error_in_mismatch() {
|
|||||||
let _ = checked_call_vm!(local_vm, <_>::default(), &script, "", result.data);
|
let _ = checked_call_vm!(local_vm, <_>::default(), &script, "", result.data);
|
||||||
|
|
||||||
let actual_value = (*args.borrow()).as_ref().unwrap().clone();
|
let actual_value = (*args.borrow()).as_ref().unwrap().clone();
|
||||||
assert_eq!(actual_value, no_error_last_error_object(),);
|
assert_eq!(actual_value, no_error_object(),);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -241,7 +241,7 @@ fn non_initialized_last_error() {
|
|||||||
let _ = checked_call_vm!(vm, test_params.clone(), script, "", "");
|
let _ = checked_call_vm!(vm, test_params.clone(), script, "", "");
|
||||||
|
|
||||||
let actual_value = (*args.borrow()).as_ref().unwrap().clone();
|
let actual_value = (*args.borrow()).as_ref().unwrap().clone();
|
||||||
assert_eq!(actual_value, no_error_last_error_object(),);
|
assert_eq!(actual_value, no_error_object(),);
|
||||||
|
|
||||||
let actual_tetraplets = (*tetraplets.borrow()).as_ref().unwrap().clone();
|
let actual_tetraplets = (*tetraplets.borrow()).as_ref().unwrap().clone();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use air::no_error_last_error_object;
|
use air::no_error_object;
|
||||||
use air::ExecutionCidState;
|
use air::ExecutionCidState;
|
||||||
use air_test_framework::AirScriptExecutor;
|
use air_test_framework::AirScriptExecutor;
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
@ -198,16 +198,57 @@ fn ap_with_last_error() {
|
|||||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "vm_1_peer_id", "service_id": ""},
|
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "vm_1_peer_id", "service_id": ""},
|
||||||
"values": [
|
"values": [
|
||||||
{
|
{
|
||||||
"result": no_error_last_error_object(),
|
"result": no_error_object(),
|
||||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "", "service_id": ""},
|
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "", "service_id": ""},
|
||||||
"trace_pos": 0
|
"trace_pos": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})),
|
})),
|
||||||
unused!(
|
unused!(
|
||||||
json!([no_error_last_error_object()]),
|
json!([no_error_object()]),
|
||||||
peer = vm_1_peer_id,
|
peer = vm_1_peer_id,
|
||||||
args = [no_error_last_error_object()]
|
args = [no_error_object()]
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(actual_trace, expected_state);
|
||||||
|
assert!(result.next_peer_pks.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ap_with_error() {
|
||||||
|
let vm_1_peer_id = "vm_1_peer_id";
|
||||||
|
let mut vm_1 = create_avm(echo_call_service(), vm_1_peer_id);
|
||||||
|
|
||||||
|
let script = format!(
|
||||||
|
r#"
|
||||||
|
(seq
|
||||||
|
(ap :error: $stream)
|
||||||
|
(seq
|
||||||
|
(canon "{vm_1_peer_id}" $stream #canon_stream)
|
||||||
|
(call "{vm_1_peer_id}" ("" "") [#canon_stream])))
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(vm_1, <_>::default(), script, "", "");
|
||||||
|
|
||||||
|
let actual_trace = trace_from_result(&result);
|
||||||
|
let expected_state = vec![
|
||||||
|
executed_state::ap(0),
|
||||||
|
executed_state::canon(json!({
|
||||||
|
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "vm_1_peer_id", "service_id": ""},
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"result": no_error_object(),
|
||||||
|
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "", "service_id": ""},
|
||||||
|
"trace_pos": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})),
|
||||||
|
unused!(
|
||||||
|
json!([no_error_object()]),
|
||||||
|
peer = vm_1_peer_id,
|
||||||
|
args = [no_error_object()]
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -589,11 +630,11 @@ fn ap_stream_map_with_undefined_last_error() {
|
|||||||
SubTraceDesc::new(3.into(), 0),
|
SubTraceDesc::new(3.into(), 0),
|
||||||
)]),
|
)]),
|
||||||
unused!(
|
unused!(
|
||||||
no_error_last_error_object(),
|
no_error_object(),
|
||||||
peer = vm_1_peer_id,
|
peer = vm_1_peer_id,
|
||||||
service = "m",
|
service = "m",
|
||||||
function = "f",
|
function = "f",
|
||||||
args = [no_error_last_error_object()]
|
args = [no_error_object()]
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -451,3 +451,35 @@ fn match_with_undefined_last_error_message() {
|
|||||||
)]);
|
)]);
|
||||||
assert_eq!(actual_trace, expected_trace);
|
assert_eq!(actual_trace, expected_trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_with_error() {
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let mut vm = create_avm(echo_call_service(), local_peer_id);
|
||||||
|
|
||||||
|
let script = format!(
|
||||||
|
r#"
|
||||||
|
(xor
|
||||||
|
(match 1 2 (null))
|
||||||
|
(call "local_peer_id" ("test" "error_code") [:error:.$.error_code] scalar)
|
||||||
|
)
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(vm, <_>::default(), script, "", "");
|
||||||
|
|
||||||
|
let actual_trace = trace_from_result(&result);
|
||||||
|
|
||||||
|
let mut cid_state = ExecutionCidState::new();
|
||||||
|
let errcode_lambda_output = json!(10001);
|
||||||
|
|
||||||
|
let expected_trace = ExecutionTrace::from(vec![scalar_tracked!(
|
||||||
|
errcode_lambda_output.clone(),
|
||||||
|
cid_state,
|
||||||
|
peer = local_peer_id,
|
||||||
|
service = "test",
|
||||||
|
function = "error_code",
|
||||||
|
args = vec![errcode_lambda_output]
|
||||||
|
)]);
|
||||||
|
assert_eq!(actual_trace, expected_trace);
|
||||||
|
}
|
||||||
|
@ -203,3 +203,37 @@ fn mismatch_with_two_xors() {
|
|||||||
|
|
||||||
assert_eq!(actual_trace.pop().unwrap(), expected_executed_call_result);
|
assert_eq!(actual_trace.pop().unwrap(), expected_executed_call_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mismatch_with_error() {
|
||||||
|
use air::ExecutionCidState;
|
||||||
|
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let mut vm = create_avm(echo_call_service(), local_peer_id);
|
||||||
|
|
||||||
|
let script = format!(
|
||||||
|
r#"
|
||||||
|
(xor
|
||||||
|
(mismatch 42 42 (null))
|
||||||
|
(call "local_peer_id" ("test" "error_code") [:error:.$.error_code] scalar)
|
||||||
|
)
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(vm, <_>::default(), script, "", "");
|
||||||
|
|
||||||
|
let actual_trace = trace_from_result(&result);
|
||||||
|
|
||||||
|
let mut cid_state = ExecutionCidState::new();
|
||||||
|
let errcode_lambda_output = json!(10002);
|
||||||
|
|
||||||
|
let expected_trace = ExecutionTrace::from(vec![scalar_tracked!(
|
||||||
|
errcode_lambda_output.clone(),
|
||||||
|
cid_state,
|
||||||
|
peer = local_peer_id,
|
||||||
|
service = "test",
|
||||||
|
function = "error_code",
|
||||||
|
args = vec![errcode_lambda_output]
|
||||||
|
)]);
|
||||||
|
assert_eq!(actual_trace, expected_trace);
|
||||||
|
}
|
||||||
|
@ -235,3 +235,34 @@ fn last_error_with_xor() {
|
|||||||
|
|
||||||
assert_eq!(actual_trace[1.into()], expected_state);
|
assert_eq!(actual_trace[1.into()], expected_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn error_with_xor() {
|
||||||
|
let faillible_peer_id = "failible_peer_id";
|
||||||
|
let mut faillible_vm = create_avm(fallible_call_service("service_id_1"), faillible_peer_id);
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let mut vm = create_avm(echo_call_service(), local_peer_id);
|
||||||
|
|
||||||
|
let script = format!(
|
||||||
|
r#"
|
||||||
|
(xor
|
||||||
|
(call "{faillible_peer_id}" ("service_id_1" "local_fn_name") [] result)
|
||||||
|
(call "{local_peer_id}" ("service_id_2" "local_fn_name") [:error:.$.message] result)
|
||||||
|
)"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(faillible_vm, <_>::default(), script.clone(), "", "");
|
||||||
|
let result = checked_call_vm!(vm, <_>::default(), script, "", result.data);
|
||||||
|
|
||||||
|
let actual_trace = trace_from_result(&result);
|
||||||
|
let msg = r#"Local service error, ret_code is 1, error message is '"failed result from fallible_call_service"'"#;
|
||||||
|
let expected_state = scalar!(
|
||||||
|
msg,
|
||||||
|
peer = local_peer_id,
|
||||||
|
service = "service_id_2",
|
||||||
|
function = "local_fn_name",
|
||||||
|
args = [msg]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(actual_trace[1.into()], expected_state);
|
||||||
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use air::no_error_last_error_object;
|
use air::no_error_object;
|
||||||
use air::unsupported_map_key_type;
|
use air::unsupported_map_key_type;
|
||||||
use air::CatchableError;
|
use air::CatchableError;
|
||||||
use air::LambdaError;
|
use air::LambdaError;
|
||||||
@ -608,7 +608,55 @@ fn undefined_last_error_functor() {
|
|||||||
.expect("invalid test AIR script");
|
.expect("invalid test AIR script");
|
||||||
let result = executor.execute_all(local_peer_id).unwrap();
|
let result = executor.execute_all(local_peer_id).unwrap();
|
||||||
|
|
||||||
let expected_error = CatchableError::LengthFunctorAppliedToNotArray(no_error_last_error_object());
|
let expected_error = CatchableError::LengthFunctorAppliedToNotArray(no_error_object());
|
||||||
|
assert!(check_error(&result.last().unwrap(), expected_error));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn undefined_error_functor() {
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let script = format!(
|
||||||
|
r#"
|
||||||
|
(xor
|
||||||
|
(match 1 2
|
||||||
|
(null)
|
||||||
|
)
|
||||||
|
(call "local_peer_id" ("test" "error_code") [:error:.length] scalar)
|
||||||
|
)
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(local_peer_id), &script)
|
||||||
|
.expect("invalid test AIR script");
|
||||||
|
let result = executor.execute_all(local_peer_id).unwrap();
|
||||||
|
|
||||||
|
let error_object = json!({"error_code":10001, "instruction":"match 1 2","message":"compared values do not match"});
|
||||||
|
let expected_error = CatchableError::LengthFunctorAppliedToNotArray(error_object);
|
||||||
|
assert!(check_error(&result.last().unwrap(), expected_error));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn undefined_error_peerid() {
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let script = format!(
|
||||||
|
r#"
|
||||||
|
(xor
|
||||||
|
(match 1 2
|
||||||
|
(null)
|
||||||
|
)
|
||||||
|
(call "local_peer_id" ("test" "error_code") [:error:.$.peerid] scalar)
|
||||||
|
)
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(local_peer_id), &script)
|
||||||
|
.expect("invalid test AIR script");
|
||||||
|
let result = executor.execute_all(local_peer_id).unwrap();
|
||||||
|
|
||||||
|
let value = json!({"error_code":10001, "instruction":"match 1 2", "message":"compared values do not match"});
|
||||||
|
let field_name = "peerid".into();
|
||||||
|
let expected_error =
|
||||||
|
air::CatchableError::LambdaApplierError(air::LambdaError::ValueNotContainSuchField { value, field_name });
|
||||||
assert!(check_error(&result.last().unwrap(), expected_error));
|
assert!(check_error(&result.last().unwrap(), expected_error));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -631,7 +679,7 @@ fn undefined_last_error_instruction() {
|
|||||||
let result = executor.execute_all(local_peer_id).unwrap();
|
let result = executor.execute_all(local_peer_id).unwrap();
|
||||||
|
|
||||||
let expected_error = CatchableError::LambdaApplierError(LambdaError::ValueNotContainSuchField {
|
let expected_error = CatchableError::LambdaApplierError(LambdaError::ValueNotContainSuchField {
|
||||||
value: no_error_last_error_object(),
|
value: no_error_object(),
|
||||||
field_name: "instruction".to_string(),
|
field_name: "instruction".to_string(),
|
||||||
});
|
});
|
||||||
assert!(check_error(&&result.last().unwrap(), expected_error));
|
assert!(check_error(&&result.last().unwrap(), expected_error));
|
||||||
@ -655,7 +703,7 @@ fn undefined_last_error_peer_id() {
|
|||||||
let result = executor.execute_all(local_peer_id).unwrap();
|
let result = executor.execute_all(local_peer_id).unwrap();
|
||||||
|
|
||||||
let expected_error = CatchableError::LambdaApplierError(LambdaError::ValueNotContainSuchField {
|
let expected_error = CatchableError::LambdaApplierError(LambdaError::ValueNotContainSuchField {
|
||||||
value: no_error_last_error_object(),
|
value: no_error_object(),
|
||||||
field_name: "peer_id".to_string(),
|
field_name: "peer_id".to_string(),
|
||||||
});
|
});
|
||||||
assert!(check_error(&&result.last().unwrap(), expected_error));
|
assert!(check_error(&&result.last().unwrap(), expected_error));
|
||||||
|
@ -21,6 +21,7 @@ use super::CanonStream;
|
|||||||
use super::CanonStreamWithLambda;
|
use super::CanonStreamWithLambda;
|
||||||
use super::ImmutableVariable;
|
use super::ImmutableVariable;
|
||||||
use super::ImmutableVariableWithLambda;
|
use super::ImmutableVariableWithLambda;
|
||||||
|
use super::InstructionErrorAST;
|
||||||
use super::Scalar;
|
use super::Scalar;
|
||||||
use super::ScalarWithLambda;
|
use super::ScalarWithLambda;
|
||||||
use super::Stream;
|
use super::Stream;
|
||||||
@ -68,6 +69,7 @@ pub struct Triplet<'i> {
|
|||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
pub enum ImmutableValue<'i> {
|
pub enum ImmutableValue<'i> {
|
||||||
InitPeerId,
|
InitPeerId,
|
||||||
|
Error(InstructionErrorAST<'i>),
|
||||||
LastError(Option<LambdaAST<'i>>),
|
LastError(Option<LambdaAST<'i>>),
|
||||||
Timestamp,
|
Timestamp,
|
||||||
TTL,
|
TTL,
|
||||||
@ -93,6 +95,7 @@ pub enum ApArgument<'i> {
|
|||||||
InitPeerId,
|
InitPeerId,
|
||||||
Timestamp,
|
Timestamp,
|
||||||
TTL,
|
TTL,
|
||||||
|
Error(InstructionErrorAST<'i>),
|
||||||
LastError(Option<LambdaAST<'i>>),
|
LastError(Option<LambdaAST<'i>>),
|
||||||
Literal(&'i str),
|
Literal(&'i str),
|
||||||
Number(Number),
|
Number(Number),
|
||||||
|
@ -35,6 +35,7 @@ impl fmt::Display for ImmutableValue<'_> {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
InitPeerId => write!(f, "%init_peer_id%"),
|
InitPeerId => write!(f, "%init_peer_id%"),
|
||||||
|
Error(error_accessor) => display_error(f, error_accessor),
|
||||||
LastError(error_accessor) => display_last_error(f, error_accessor),
|
LastError(error_accessor) => display_last_error(f, error_accessor),
|
||||||
Literal(literal) => write!(f, r#""{literal}""#),
|
Literal(literal) => write!(f, r#""{literal}""#),
|
||||||
Timestamp => write!(f, "%timestamp%"),
|
Timestamp => write!(f, "%timestamp%"),
|
||||||
@ -93,6 +94,7 @@ impl fmt::Display for ApArgument<'_> {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
InitPeerId => write!(f, "%init_peer_id%"),
|
InitPeerId => write!(f, "%init_peer_id%"),
|
||||||
|
Error(error_accessor) => display_error(f, error_accessor),
|
||||||
LastError(error_accessor) => display_last_error(f, error_accessor),
|
LastError(error_accessor) => display_last_error(f, error_accessor),
|
||||||
Literal(str) => write!(f, r#""{str}""#),
|
Literal(str) => write!(f, r#""{str}""#),
|
||||||
Timestamp => write!(f, "%timestamp%"),
|
Timestamp => write!(f, "%timestamp%"),
|
||||||
@ -183,8 +185,21 @@ impl From<&Number> for serde_json::Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn display_last_error(f: &mut fmt::Formatter, lambda_ast: &Option<LambdaAST>) -> fmt::Result {
|
fn display_last_error(f: &mut fmt::Formatter, lambda_ast: &Option<LambdaAST>) -> fmt::Result {
|
||||||
|
use crate::parser::LAST_ERROR;
|
||||||
|
|
||||||
match lambda_ast {
|
match lambda_ast {
|
||||||
Some(lambda_ast) => write!(f, "%last_error%{lambda_ast}"),
|
Some(lambda_ast) => write!(f, "{LAST_ERROR}{lambda_ast}"),
|
||||||
None => write!(f, "%last_error%"),
|
None => write!(f, "{LAST_ERROR}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_error(f: &mut fmt::Formatter, error: &InstructionErrorAST) -> fmt::Result {
|
||||||
|
use crate::parser::ERROR;
|
||||||
|
|
||||||
|
let InstructionErrorAST { lens } = error;
|
||||||
|
|
||||||
|
match lens {
|
||||||
|
Some(lens) => write!(f, "{ERROR}{lens}"),
|
||||||
|
None => write!(f, "{ERROR}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,3 +188,7 @@ pub struct New<'i> {
|
|||||||
/// (null)
|
/// (null)
|
||||||
#[derive(Serialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||||
pub struct Null;
|
pub struct Null;
|
||||||
|
|
||||||
|
pub trait PeerIDErrorLogable {
|
||||||
|
fn log_errors_with_peer_id(&self) -> bool;
|
||||||
|
}
|
||||||
|
@ -175,3 +175,49 @@ impl fmt::Display for New<'_> {
|
|||||||
write!(f, "new {}", self.argument)
|
write!(f, "new {}", self.argument)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! peer_id_error_logable {
|
||||||
|
($($t:ty),+) => {
|
||||||
|
$(
|
||||||
|
impl PeerIDErrorLogable for $t {
|
||||||
|
#[inline]
|
||||||
|
fn log_errors_with_peer_id(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! no_peer_id_error_logable {
|
||||||
|
($($t:ty),+) => {
|
||||||
|
$(
|
||||||
|
impl PeerIDErrorLogable for $t {
|
||||||
|
#[inline]
|
||||||
|
fn log_errors_with_peer_id(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
peer_id_error_logable!(Call<'_>, Canon<'_>, CanonStreamMapScalar<'_>);
|
||||||
|
|
||||||
|
no_peer_id_error_logable!(
|
||||||
|
Ap<'_>,
|
||||||
|
ApMap<'_>,
|
||||||
|
Fail<'_>,
|
||||||
|
FoldScalar<'_>,
|
||||||
|
FoldStream<'_>,
|
||||||
|
FoldStreamMap<'_>,
|
||||||
|
Seq<'_>,
|
||||||
|
Par<'_>,
|
||||||
|
Xor<'_>,
|
||||||
|
Match<'_>,
|
||||||
|
MisMatch<'_>,
|
||||||
|
Never,
|
||||||
|
Next<'_>,
|
||||||
|
New<'_>,
|
||||||
|
Null
|
||||||
|
);
|
||||||
|
@ -86,3 +86,10 @@ pub struct StreamMap<'i> {
|
|||||||
pub name: &'i str,
|
pub name: &'i str,
|
||||||
pub position: AirPos,
|
pub position: AirPos,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error wrapper with an optional lens.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct InstructionErrorAST<'lens> {
|
||||||
|
#[serde(borrow)]
|
||||||
|
pub lens: Option<LambdaAST<'lens>>,
|
||||||
|
}
|
||||||
|
@ -125,3 +125,9 @@ impl<'i> StreamMap<'i> {
|
|||||||
Self { name, position }
|
Self { name, position }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lens> InstructionErrorAST<'lens> {
|
||||||
|
pub fn new(lens: Option<LambdaAST<'lens>>) -> Self {
|
||||||
|
Self { lens }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -230,6 +230,8 @@ Value: ImmutableValue<'input> = {
|
|||||||
InitPeerId => ImmutableValue::InitPeerId,
|
InitPeerId => ImmutableValue::InitPeerId,
|
||||||
<LastError> => ImmutableValue::LastError(None),
|
<LastError> => ImmutableValue::LastError(None),
|
||||||
<le:LastErrorWithLambda> => ImmutableValue::LastError(Some(le)),
|
<le:LastErrorWithLambda> => ImmutableValue::LastError(Some(le)),
|
||||||
|
<Error> => ImmutableValue::Error(InstructionErrorAST::new(None)),
|
||||||
|
<le:ErrorWithLambda> => ImmutableValue::Error(InstructionErrorAST::new(Some(le))),
|
||||||
<l:Literal> => ImmutableValue::Literal(l),
|
<l:Literal> => ImmutableValue::Literal(l),
|
||||||
Timestamp => ImmutableValue::Timestamp,
|
Timestamp => ImmutableValue::Timestamp,
|
||||||
TTL => ImmutableValue::TTL,
|
TTL => ImmutableValue::TTL,
|
||||||
@ -246,6 +248,8 @@ ApArgument: ApArgument<'input> = {
|
|||||||
InitPeerId => ApArgument::InitPeerId,
|
InitPeerId => ApArgument::InitPeerId,
|
||||||
<LastError> => ApArgument::LastError(None),
|
<LastError> => ApArgument::LastError(None),
|
||||||
<le:LastErrorWithLambda> => ApArgument::LastError(Some(le)),
|
<le:LastErrorWithLambda> => ApArgument::LastError(Some(le)),
|
||||||
|
<Error> => ApArgument::Error(InstructionErrorAST::new(None)),
|
||||||
|
<le:ErrorWithLambda> => ApArgument::Error(InstructionErrorAST::new(Some(le))),
|
||||||
Timestamp => ApArgument::Timestamp,
|
Timestamp => ApArgument::Timestamp,
|
||||||
TTL => ApArgument::TTL,
|
TTL => ApArgument::TTL,
|
||||||
<l:Literal> => ApArgument::Literal(l),
|
<l:Literal> => ApArgument::Literal(l),
|
||||||
@ -295,6 +299,8 @@ extern {
|
|||||||
InitPeerId => Token::InitPeerId,
|
InitPeerId => Token::InitPeerId,
|
||||||
LastError => Token::LastError,
|
LastError => Token::LastError,
|
||||||
LastErrorWithLambda => Token::LastErrorWithLambda(<LambdaAST<'input>>),
|
LastErrorWithLambda => Token::LastErrorWithLambda(<LambdaAST<'input>>),
|
||||||
|
Error => Token::Error,
|
||||||
|
ErrorWithLambda => Token::ErrorWithLambda(<LambdaAST<'input>>),
|
||||||
Timestamp => Token::Timestamp,
|
Timestamp => Token::Timestamp,
|
||||||
TTL => Token::TTL,
|
TTL => Token::TTL,
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -195,7 +195,10 @@ fn string_to_token(input: &str, start_pos: AirPos) -> LexerResult<Token> {
|
|||||||
MISMATCH_INSTR => Ok(Token::MisMatch),
|
MISMATCH_INSTR => Ok(Token::MisMatch),
|
||||||
|
|
||||||
INIT_PEER_ID => Ok(Token::InitPeerId),
|
INIT_PEER_ID => Ok(Token::InitPeerId),
|
||||||
_ if input.starts_with(LAST_ERROR) => parse_last_error(input, start_pos),
|
_ if input.starts_with(ERROR) => parse_error(input, start_pos, ERROR, Token::Error),
|
||||||
|
_ if input.starts_with(LAST_ERROR) => {
|
||||||
|
parse_error(input, start_pos, LAST_ERROR, Token::LastError)
|
||||||
|
}
|
||||||
TIMESTAMP => Ok(Token::Timestamp),
|
TIMESTAMP => Ok(Token::Timestamp),
|
||||||
TTL => Ok(Token::TTL),
|
TTL => Ok(Token::TTL),
|
||||||
|
|
||||||
@ -206,28 +209,37 @@ fn string_to_token(input: &str, start_pos: AirPos) -> LexerResult<Token> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_last_error(input: &str, start_pos: AirPos) -> LexerResult<Token<'_>> {
|
fn parse_error<'input>(
|
||||||
let last_error_size = LAST_ERROR.len();
|
input: &'input str,
|
||||||
if input.len() == last_error_size {
|
start_pos: AirPos,
|
||||||
return Ok(Token::LastError);
|
token_str: &str,
|
||||||
|
token_wo_lens: Token<'static>,
|
||||||
|
) -> LexerResult<Token<'input>> {
|
||||||
|
let token_wo_lens_len = token_str.len();
|
||||||
|
|
||||||
|
if input.len() == token_wo_lens_len {
|
||||||
|
return Ok(token_wo_lens);
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.len() <= last_error_size {
|
if input.len() <= token_wo_lens_len {
|
||||||
return Err(LexerError::lambda_parser_error(
|
return Err(LexerError::lambda_parser_error(
|
||||||
start_pos + last_error_size..start_pos + input.len(),
|
start_pos + token_wo_lens_len..start_pos + input.len(),
|
||||||
"lambda AST applied to last error has not enough size",
|
"lambda AST applied to last error has not enough size",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let last_error_accessor = crate::parse_lambda(&input[last_error_size..]).map_err(|e| {
|
let last_error_accessor = crate::parse_lambda(&input[token_wo_lens_len..]).map_err(|e| {
|
||||||
LexerError::lambda_parser_error(
|
LexerError::lambda_parser_error(
|
||||||
start_pos + last_error_size..start_pos + input.len(),
|
start_pos + token_wo_lens_len..start_pos + input.len(),
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let last_error_token = Token::LastErrorWithLambda(last_error_accessor);
|
|
||||||
|
|
||||||
Ok(last_error_token)
|
match token_wo_lens {
|
||||||
|
Token::Error => Ok(Token::ErrorWithLambda(last_error_accessor)),
|
||||||
|
Token::LastError => Ok(Token::LastErrorWithLambda(last_error_accessor)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CALL_INSTR: &str = "call";
|
const CALL_INSTR: &str = "call";
|
||||||
@ -246,7 +258,8 @@ const MATCH_INSTR: &str = "match";
|
|||||||
const MISMATCH_INSTR: &str = "mismatch";
|
const MISMATCH_INSTR: &str = "mismatch";
|
||||||
|
|
||||||
const INIT_PEER_ID: &str = "%init_peer_id%";
|
const INIT_PEER_ID: &str = "%init_peer_id%";
|
||||||
const LAST_ERROR: &str = "%last_error%";
|
pub(crate) const LAST_ERROR: &str = "%last_error%";
|
||||||
|
pub(crate) const ERROR: &str = ":error:";
|
||||||
const TIMESTAMP: &str = "%timestamp%";
|
const TIMESTAMP: &str = "%timestamp%";
|
||||||
const TTL: &str = "%ttl%";
|
const TTL: &str = "%ttl%";
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ mod tests;
|
|||||||
pub mod text_pos;
|
pub mod text_pos;
|
||||||
|
|
||||||
pub use air_lexer::AIRLexer;
|
pub use air_lexer::AIRLexer;
|
||||||
|
pub(crate) use air_lexer::ERROR;
|
||||||
|
pub(crate) use air_lexer::LAST_ERROR;
|
||||||
pub use errors::LexerError;
|
pub use errors::LexerError;
|
||||||
pub use text_pos::AirPos;
|
pub use text_pos::AirPos;
|
||||||
pub use token::Token;
|
pub use token::Token;
|
||||||
|
@ -72,7 +72,9 @@ pub enum Token<'input> {
|
|||||||
|
|
||||||
InitPeerId,
|
InitPeerId,
|
||||||
LastError,
|
LastError,
|
||||||
|
Error,
|
||||||
LastErrorWithLambda(LambdaAST<'input>),
|
LastErrorWithLambda(LambdaAST<'input>),
|
||||||
|
ErrorWithLambda(LambdaAST<'input>),
|
||||||
Timestamp,
|
Timestamp,
|
||||||
TTL,
|
TTL,
|
||||||
|
|
||||||
|
@ -33,6 +33,8 @@ pub mod tests;
|
|||||||
pub use self::air_parser::parse;
|
pub use self::air_parser::parse;
|
||||||
pub use air::AIRParser;
|
pub use air::AIRParser;
|
||||||
pub use lexer::AIRLexer;
|
pub use lexer::AIRLexer;
|
||||||
|
pub(crate) use lexer::ERROR;
|
||||||
|
pub(crate) use lexer::LAST_ERROR;
|
||||||
pub use span::Span;
|
pub use span::Span;
|
||||||
pub use validator::VariableValidator;
|
pub use validator::VariableValidator;
|
||||||
|
|
||||||
|
@ -155,6 +155,7 @@ impl<'i> VariableValidator<'i> {
|
|||||||
| ApArgument::Literal(_)
|
| ApArgument::Literal(_)
|
||||||
| ApArgument::EmptyArray
|
| ApArgument::EmptyArray
|
||||||
| ApArgument::LastError(_) => {}
|
| ApArgument::LastError(_) => {}
|
||||||
|
ApArgument::Error(_) => {}
|
||||||
ApArgument::Scalar(scalar) => self.met_scalar(scalar, span),
|
ApArgument::Scalar(scalar) => self.met_scalar(scalar, span),
|
||||||
ApArgument::ScalarWithLambda(scalar) => self.met_scalar_wl(scalar, span),
|
ApArgument::ScalarWithLambda(scalar) => self.met_scalar_wl(scalar, span),
|
||||||
ApArgument::CanonStream(canon_stream) => self.met_canon_stream(canon_stream, span),
|
ApArgument::CanonStream(canon_stream) => self.met_canon_stream(canon_stream, span),
|
||||||
@ -227,8 +228,8 @@ impl<'i> VariableValidator<'i> {
|
|||||||
use ImmutableValue::*;
|
use ImmutableValue::*;
|
||||||
|
|
||||||
match instr_arg_value {
|
match instr_arg_value {
|
||||||
InitPeerId | LastError(_) | Timestamp | TTL | Literal(_) | Number(_) | Boolean(_)
|
InitPeerId | Error(_) | LastError(_) | Timestamp | TTL | Literal(_) | Number(_)
|
||||||
| EmptyArray => {}
|
| Boolean(_) | EmptyArray => {}
|
||||||
Variable(variable) => self.met_variable(variable, span),
|
Variable(variable) => self.met_variable(variable, span),
|
||||||
VariableWithLambda(variable) => self.met_variable_wl(variable, span),
|
VariableWithLambda(variable) => self.met_variable_wl(variable, span),
|
||||||
}
|
}
|
||||||
@ -323,6 +324,7 @@ impl<'i> VariableValidator<'i> {
|
|||||||
| ImmutableValue::Number(_)
|
| ImmutableValue::Number(_)
|
||||||
| ImmutableValue::Boolean(_)
|
| ImmutableValue::Boolean(_)
|
||||||
| ImmutableValue::Literal(_)
|
| ImmutableValue::Literal(_)
|
||||||
|
| ImmutableValue::Error(_)
|
||||||
| ImmutableValue::LastError(_)
|
| ImmutableValue::LastError(_)
|
||||||
| ImmutableValue::EmptyArray => {}
|
| ImmutableValue::EmptyArray => {}
|
||||||
ImmutableValue::Variable(variable) => self.met_variable(variable, span),
|
ImmutableValue::Variable(variable) => self.met_variable(variable, span),
|
||||||
|
@ -135,7 +135,10 @@ fn parse_sexp_string(inp: Input<'_>) -> IResult<Input<'_>, Sexp, ParseError<'_>>
|
|||||||
fn parse_sexp_symbol(inp: Input<'_>) -> IResult<Input<'_>, Sexp, ParseError<'_>> {
|
fn parse_sexp_symbol(inp: Input<'_>) -> IResult<Input<'_>, Sexp, ParseError<'_>> {
|
||||||
map(
|
map(
|
||||||
recognize(pair(
|
recognize(pair(
|
||||||
many1_count(alt((value((), alphanumeric1), value((), one_of("_-.$#%"))))),
|
many1_count(alt((
|
||||||
|
value((), alphanumeric1),
|
||||||
|
value((), one_of("_-.:$#%")),
|
||||||
|
))),
|
||||||
opt(terminated(
|
opt(terminated(
|
||||||
delimited(tag("["), parse_sexp_symbol, tag("]")),
|
delimited(tag("["), parse_sexp_symbol, tag("]")),
|
||||||
opt(tag("!")),
|
opt(tag("!")),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user