mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 23:20:18 +00:00
Refactor execution errors (#198)
This commit is contained in:
parent
54e383cdaf
commit
f69b5aa728
@ -44,13 +44,13 @@ fn match_position_variable(
|
|||||||
generation: Option<u32>,
|
generation: Option<u32>,
|
||||||
ap_result: &MergerApResult,
|
ap_result: &MergerApResult,
|
||||||
) -> ExecutionResult<()> {
|
) -> ExecutionResult<()> {
|
||||||
use crate::execution_step::ExecutionError::ApResultNotCorrespondToInstr;
|
use crate::execution_step::UncatchableError::ApResultNotCorrespondToInstr;
|
||||||
use ast::Variable::*;
|
use ast::Variable::*;
|
||||||
|
|
||||||
match (variable, generation) {
|
match (variable, generation) {
|
||||||
(Stream(_), Some(_)) => Ok(()),
|
(Stream(_), Some(_)) => Ok(()),
|
||||||
(Scalar(_), None) => Ok(()),
|
(Scalar(_), None) => Ok(()),
|
||||||
_ => return crate::exec_err!(ApResultNotCorrespondToInstr(ap_result.clone())),
|
_ => Err(ApResultNotCorrespondToInstr(ap_result.clone()).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,25 +40,26 @@ impl<'i> super::ExecutableInstruction<'i> for Call<'i> {
|
|||||||
log_instruction!(call, exec_ctx, trace_ctx);
|
log_instruction!(call, exec_ctx, trace_ctx);
|
||||||
exec_ctx.tracker.meet_call();
|
exec_ctx.tracker.meet_call();
|
||||||
|
|
||||||
let resolved_call = joinable!(ResolvedCall::new(self, exec_ctx), exec_ctx).map_err(|e| {
|
let resolved_call = joinable!(ResolvedCall::new(self, exec_ctx), exec_ctx)
|
||||||
set_last_error(self, exec_ctx, e.clone(), None);
|
.map_err(|e| set_last_error(self, exec_ctx, e, None))?;
|
||||||
e
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let tetraplet = resolved_call.as_tetraplet();
|
let tetraplet = resolved_call.as_tetraplet();
|
||||||
joinable!(resolved_call.execute(exec_ctx, trace_ctx), exec_ctx).map_err(|e| {
|
joinable!(resolved_call.execute(exec_ctx, trace_ctx), exec_ctx)
|
||||||
set_last_error(self, exec_ctx, e.clone(), Some(tetraplet));
|
.map_err(|e| set_last_error(self, exec_ctx, e, Some(tetraplet)))
|
||||||
e
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_last_error<'i>(
|
fn set_last_error<'i>(
|
||||||
call: &Call<'i>,
|
call: &Call<'i>,
|
||||||
exec_ctx: &mut ExecutionCtx<'i>,
|
exec_ctx: &mut ExecutionCtx<'i>,
|
||||||
e: Rc<ExecutionError>,
|
execution_error: ExecutionError,
|
||||||
tetraplet: Option<RSecurityTetraplet>,
|
tetraplet: Option<RSecurityTetraplet>,
|
||||||
) {
|
) -> ExecutionError {
|
||||||
|
let catchable_error = match execution_error {
|
||||||
|
ExecutionError::Catchable(catchable) => catchable,
|
||||||
|
ExecutionError::Uncatchable(_) => return execution_error,
|
||||||
|
};
|
||||||
|
|
||||||
let current_peer_id = match &tetraplet {
|
let current_peer_id = match &tetraplet {
|
||||||
// use tetraplet if they set, because an error could be propagated from data
|
// 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
|
// (from CallServiceFailed state) and exec_ctx.current_peer_id won't mean
|
||||||
@ -67,10 +68,16 @@ fn set_last_error<'i>(
|
|||||||
None => exec_ctx.current_peer_id.to_string(),
|
None => exec_ctx.current_peer_id.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
log::warn!("call failed with an error `{}`, peerId `{}`", e, current_peer_id);
|
log::warn!(
|
||||||
|
"call failed with an error `{}`, peerId `{}`",
|
||||||
|
catchable_error,
|
||||||
|
current_peer_id
|
||||||
|
);
|
||||||
|
|
||||||
let instruction = call.to_string();
|
let instruction = call.to_string();
|
||||||
let last_error = LastErrorDescriptor::new(e, instruction, current_peer_id, tetraplet);
|
let last_error = LastErrorDescriptor::new(catchable_error.clone(), instruction, current_peer_id, tetraplet);
|
||||||
exec_ctx.last_error = Some(last_error);
|
exec_ctx.last_error = Some(last_error);
|
||||||
exec_ctx.last_error_could_be_set = false;
|
exec_ctx.last_error_could_be_set = false;
|
||||||
|
|
||||||
|
ExecutionError::Catchable(catchable_error)
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::exec_err;
|
|
||||||
use crate::execution_step::air::call::call_result_setter::set_result_from_value;
|
use crate::execution_step::air::call::call_result_setter::set_result_from_value;
|
||||||
|
use crate::execution_step::CatchableError;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
|
|
||||||
use air_interpreter_data::CallResult;
|
use air_interpreter_data::CallResult;
|
||||||
@ -51,7 +51,7 @@ pub(super) fn handle_prev_state<'i>(
|
|||||||
let ret_code = *ret_code;
|
let ret_code = *ret_code;
|
||||||
let err_msg = err_msg.clone();
|
let err_msg = err_msg.clone();
|
||||||
trace_ctx.meet_call_end(prev_result);
|
trace_ctx.meet_call_end(prev_result);
|
||||||
exec_err!(ExecutionError::LocalServiceError(ret_code, err_msg))
|
Err(CatchableError::LocalServiceError(ret_code, err_msg).into())
|
||||||
}
|
}
|
||||||
RequestSentBy(Sender::PeerIdWithCallId { peer_id, call_id })
|
RequestSentBy(Sender::PeerIdWithCallId { peer_id, call_id })
|
||||||
if peer_id.as_str() == exec_ctx.current_peer_id.as_str() =>
|
if peer_id.as_str() == exec_ctx.current_peer_id.as_str() =>
|
||||||
@ -126,12 +126,11 @@ fn handle_service_error(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let error_message = Rc::new(service_result.result);
|
let error_message = Rc::new(service_result.result);
|
||||||
let error = ExecutionError::LocalServiceError(service_result.ret_code, error_message.clone());
|
let error = CatchableError::LocalServiceError(service_result.ret_code, error_message.clone());
|
||||||
let error = Rc::new(error);
|
|
||||||
|
|
||||||
trace_ctx.meet_call_end(CallServiceFailed(service_result.ret_code, error_message));
|
trace_ctx.meet_call_end(CallServiceFailed(service_result.ret_code, error_message));
|
||||||
|
|
||||||
Err(error)
|
Err(error.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_to_service_result(
|
fn try_to_service_result(
|
||||||
@ -152,7 +151,7 @@ fn try_to_service_result(
|
|||||||
let error = CallServiceFailed(i32::MAX, error_msg.clone());
|
let error = CallServiceFailed(i32::MAX, error_msg.clone());
|
||||||
trace_ctx.meet_call_end(error);
|
trace_ctx.meet_call_end(error);
|
||||||
|
|
||||||
Err(Rc::new(ExecutionError::LocalServiceError(i32::MAX, error_msg)))
|
Err(CatchableError::LocalServiceError(i32::MAX, error_msg).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ use super::triplet::resolve;
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
use crate::execution_step::SecurityTetraplets;
|
use crate::execution_step::SecurityTetraplets;
|
||||||
|
use crate::execution_step::UncatchableError;
|
||||||
use crate::trace_to_exec_err;
|
use crate::trace_to_exec_err;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
use crate::SecurityTetraplet;
|
use crate::SecurityTetraplet;
|
||||||
@ -187,10 +188,10 @@ fn check_output_name(output: &ast::CallOutputValue<'_>, exec_ctx: &ExecutionCtx<
|
|||||||
if exec_ctx.scalars.shadowing_allowed() {
|
if exec_ctx.scalars.shadowing_allowed() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
crate::exec_err!(ExecutionError::MultipleVariablesFound(scalar_name.to_string()))
|
Err(UncatchableError::MultipleVariablesFound(scalar_name.to_string()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(ScalarRef::IterableValue(_)) => crate::exec_err!(ExecutionError::IterableShadowing(scalar_name.to_string())),
|
Ok(ScalarRef::IterableValue(_)) => Err(UncatchableError::IterableShadowing(scalar_name.to_string()).into()),
|
||||||
Err(_) => Ok(()),
|
Err(_) => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::ExecutionCtx;
|
use super::ExecutionCtx;
|
||||||
use super::ExecutionError;
|
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use crate::exec_err;
|
use crate::execution_step::CatchableError;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
|
|
||||||
use air_parser::ast;
|
use air_parser::ast;
|
||||||
@ -63,10 +62,11 @@ fn resolve_to_string<'i>(value: &ast::CallInstrValue<'i>, ctx: &ExecutionCtx<'i>
|
|||||||
fn try_jvalue_to_string(jvalue: JValue, variable: &ast::VariableWithLambda<'_>) -> ExecutionResult<String> {
|
fn try_jvalue_to_string(jvalue: JValue, variable: &ast::VariableWithLambda<'_>) -> ExecutionResult<String> {
|
||||||
match jvalue {
|
match jvalue {
|
||||||
JValue::String(s) => Ok(s),
|
JValue::String(s) => Ok(s),
|
||||||
_ => exec_err!(ExecutionError::IncompatibleJValueType {
|
_ => Err(CatchableError::IncompatibleJValueType {
|
||||||
variable_name: variable.name().to_string(),
|
variable_name: variable.name().to_string(),
|
||||||
actual_value: jvalue,
|
actual_value: jvalue,
|
||||||
expected_value_type: "string",
|
expected_value_type: "string",
|
||||||
}),
|
}
|
||||||
|
.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::ExecutionCtx;
|
use super::ExecutionCtx;
|
||||||
use super::ExecutionError;
|
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::LastErrorDescriptor;
|
use super::LastErrorDescriptor;
|
||||||
use super::TraceHandler;
|
use super::TraceHandler;
|
||||||
|
use crate::execution_step::CatchableError;
|
||||||
use crate::log_instruction;
|
use crate::log_instruction;
|
||||||
|
|
||||||
use air_parser::ast::Fail;
|
use air_parser::ast::Fail;
|
||||||
@ -48,7 +48,7 @@ fn fail_with_literals<'i>(
|
|||||||
fail: &Fail<'_>,
|
fail: &Fail<'_>,
|
||||||
exec_ctx: &mut ExecutionCtx<'i>,
|
exec_ctx: &mut ExecutionCtx<'i>,
|
||||||
) -> ExecutionResult<()> {
|
) -> ExecutionResult<()> {
|
||||||
let fail_error = ExecutionError::FailWithoutXorError {
|
let fail_error = CatchableError::FailWithoutXorError {
|
||||||
ret_code,
|
ret_code,
|
||||||
error_message: error_message.to_string(),
|
error_message: error_message.to_string(),
|
||||||
};
|
};
|
||||||
@ -71,7 +71,7 @@ fn fail_with_literals<'i>(
|
|||||||
|
|
||||||
update_context_state(exec_ctx);
|
update_context_state(exec_ctx);
|
||||||
|
|
||||||
Err(fail_error)
|
Err(fail_error.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fail_with_last_error(exec_ctx: &mut ExecutionCtx<'_>) -> ExecutionResult<()> {
|
fn fail_with_last_error(exec_ctx: &mut ExecutionCtx<'_>) -> ExecutionResult<()> {
|
||||||
@ -81,7 +81,7 @@ fn fail_with_last_error(exec_ctx: &mut ExecutionCtx<'_>) -> ExecutionResult<()>
|
|||||||
};
|
};
|
||||||
|
|
||||||
update_context_state(exec_ctx);
|
update_context_state(exec_ctx);
|
||||||
Err(last_error)
|
Err(last_error.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_context_state(exec_ctx: &mut ExecutionCtx<'_>) {
|
fn update_context_state(exec_ctx: &mut ExecutionCtx<'_>) {
|
||||||
|
@ -22,7 +22,6 @@ pub(crate) use fold_state::IterableType;
|
|||||||
pub(super) use utils::*;
|
pub(super) use utils::*;
|
||||||
|
|
||||||
use super::ExecutionCtx;
|
use super::ExecutionCtx;
|
||||||
use super::ExecutionError;
|
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::Instruction;
|
use super::Instruction;
|
||||||
use super::ScalarRef;
|
use super::ScalarRef;
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::exec_err;
|
use crate::execution_step::CatchableError;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
use crate::LambdaAST;
|
use crate::LambdaAST;
|
||||||
@ -107,11 +107,12 @@ fn from_call_result(call_result: ValueAggregate, variable_name: &str) -> Executi
|
|||||||
array.len()
|
array.len()
|
||||||
}
|
}
|
||||||
v => {
|
v => {
|
||||||
return exec_err!(ExecutionError::IncompatibleJValueType {
|
return Err(CatchableError::IncompatibleJValueType {
|
||||||
variable_name: variable_name.to_string(),
|
variable_name: variable_name.to_string(),
|
||||||
actual_value: (*v).clone(),
|
actual_value: (*v).clone(),
|
||||||
expected_value_type: "array",
|
expected_value_type: "array",
|
||||||
})
|
}
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -156,10 +157,7 @@ fn from_jvalue(
|
|||||||
let iterable = match jvalue {
|
let iterable = match jvalue {
|
||||||
JValue::Array(array) => array,
|
JValue::Array(array) => array,
|
||||||
_ => {
|
_ => {
|
||||||
return exec_err!(ExecutionError::FoldIteratesOverNonArray(
|
return Err(CatchableError::FoldIteratesOverNonArray(jvalue.clone(), formatted_lambda_ast).into());
|
||||||
jvalue.clone(),
|
|
||||||
formatted_lambda_ast
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
use super::compare_matchable::are_matchable_eq;
|
use super::compare_matchable::are_matchable_eq;
|
||||||
use super::ExecutionCtx;
|
use super::ExecutionCtx;
|
||||||
use super::ExecutionError;
|
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::TraceHandler;
|
use super::TraceHandler;
|
||||||
|
use crate::execution_step::CatchableError;
|
||||||
use crate::execution_step::Joinable;
|
use crate::execution_step::Joinable;
|
||||||
use crate::joinable;
|
use crate::joinable;
|
||||||
use crate::log_instruction;
|
use crate::log_instruction;
|
||||||
@ -35,7 +35,7 @@ impl<'i> super::ExecutableInstruction<'i> for Match<'i> {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !are_values_equal {
|
if !are_values_equal {
|
||||||
return crate::exec_err!(ExecutionError::MatchWithoutXorError);
|
return Err(CatchableError::MatchWithoutXorError.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.instruction.execute(exec_ctx, trace_ctx)
|
self.instruction.execute(exec_ctx, trace_ctx)
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
use super::compare_matchable::are_matchable_eq;
|
use super::compare_matchable::are_matchable_eq;
|
||||||
use super::ExecutionCtx;
|
use super::ExecutionCtx;
|
||||||
use super::ExecutionError;
|
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::TraceHandler;
|
use super::TraceHandler;
|
||||||
|
use crate::execution_step::CatchableError;
|
||||||
use crate::execution_step::Joinable;
|
use crate::execution_step::Joinable;
|
||||||
use crate::joinable;
|
use crate::joinable;
|
||||||
use crate::log_instruction;
|
use crate::log_instruction;
|
||||||
@ -35,7 +35,7 @@ impl<'i> super::ExecutableInstruction<'i> for MisMatch<'i> {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
if are_values_equal {
|
if are_values_equal {
|
||||||
return crate::exec_err!(ExecutionError::MismatchWithoutXorError);
|
return Err(CatchableError::MismatchWithoutXorError.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.instruction.execute(exec_ctx, trace_ctx)
|
self.instruction.execute(exec_ctx, trace_ctx)
|
||||||
|
@ -35,7 +35,6 @@ pub(crate) use fold::FoldState;
|
|||||||
use super::boxed_value::ScalarRef;
|
use super::boxed_value::ScalarRef;
|
||||||
use super::boxed_value::ValueAggregate;
|
use super::boxed_value::ValueAggregate;
|
||||||
use super::execution_context::*;
|
use super::execution_context::*;
|
||||||
use super::Catchable;
|
|
||||||
use super::ExecutionCtx;
|
use super::ExecutionCtx;
|
||||||
use super::ExecutionError;
|
use super::ExecutionError;
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
@ -47,48 +46,68 @@ use air_parser::ast::Instruction;
|
|||||||
|
|
||||||
/// Executes instruction and updates last error if needed.
|
/// 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) {
|
let result = $instr.execute($exec_ctx, $trace_ctx);
|
||||||
Err(e) => {
|
|
||||||
if !$exec_ctx.last_error_could_be_set {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
let instruction = format!("{}", $self);
|
if !$exec_ctx.last_error_could_be_set {
|
||||||
let last_error =
|
return result;
|
||||||
LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.to_string(), None);
|
|
||||||
$exec_ctx.last_error = Some(last_error);
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
v => v,
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
let execution_error = match result {
|
||||||
|
Err(e) => e,
|
||||||
|
v => return v,
|
||||||
|
};
|
||||||
|
|
||||||
|
let catchable_error = match execution_error {
|
||||||
|
// handle only catchable errors
|
||||||
|
crate::execution_step::ExecutionError::Catchable(e) => e,
|
||||||
|
e => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let instruction = $self.to_string();
|
||||||
|
let last_error = LastErrorDescriptor::new(
|
||||||
|
catchable_error.clone(),
|
||||||
|
instruction,
|
||||||
|
$exec_ctx.current_peer_id.to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
$exec_ctx.last_error = Some(last_error);
|
||||||
|
|
||||||
|
Err(catchable_error.into())
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes match/mismatch instructions and updates last error if error type wasn't
|
/// Executes match/mismatch instructions and updates last error if error type wasn't
|
||||||
/// MatchWithoutXorError or MismatchWithoutXorError.
|
/// MatchWithoutXorError or MismatchWithoutXorError.
|
||||||
macro_rules! execute_match_mismatch {
|
macro_rules! execute_match_or_mismatch {
|
||||||
($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) {
|
let result = $instr.execute($exec_ctx, $trace_ctx);
|
||||||
Err(e) => {
|
let execution_error = match result {
|
||||||
use std::borrow::Borrow;
|
Err(e) => e,
|
||||||
|
v => return v,
|
||||||
|
};
|
||||||
|
|
||||||
if !$exec_ctx.last_error_could_be_set
|
if !$exec_ctx.last_error_could_be_set || execution_error.is_match_or_mismatch() {
|
||||||
|| matches!(&*e.borrow(), ExecutionError::MatchWithoutXorError)
|
return Err(execution_error);
|
||||||
|| matches!(&*e.borrow(), ExecutionError::MismatchWithoutXorError)
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
let catchable_error = match execution_error {
|
||||||
|
// handle only catchable errors
|
||||||
|
crate::execution_step::ExecutionError::Catchable(e) => e,
|
||||||
|
e => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let instruction = $self.to_string();
|
||||||
|
let last_error = LastErrorDescriptor::new(
|
||||||
|
catchable_error.clone(),
|
||||||
|
instruction,
|
||||||
|
$exec_ctx.current_peer_id.to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
$exec_ctx.last_error = Some(last_error);
|
||||||
|
|
||||||
|
Err(catchable_error.into())
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait ExecutableInstruction<'i> {
|
pub(crate) trait ExecutableInstruction<'i> {
|
||||||
@ -114,8 +133,8 @@ impl<'i> ExecutableInstruction<'i> for Instruction<'i> {
|
|||||||
Instruction::Xor(xor) => execute!(self, xor, exec_ctx, trace_ctx),
|
Instruction::Xor(xor) => execute!(self, xor, exec_ctx, trace_ctx),
|
||||||
|
|
||||||
// match/mismatch shouldn't rewrite last_error
|
// match/mismatch shouldn't rewrite last_error
|
||||||
Instruction::Match(match_) => execute_match_mismatch!(self, match_, exec_ctx, trace_ctx),
|
Instruction::Match(match_) => execute_match_or_mismatch!(self, match_, exec_ctx, trace_ctx),
|
||||||
Instruction::MisMatch(mismatch) => execute_match_mismatch!(self, mismatch, exec_ctx, trace_ctx),
|
Instruction::MisMatch(mismatch) => execute_match_or_mismatch!(self, mismatch, exec_ctx, trace_ctx),
|
||||||
|
|
||||||
Instruction::Error => unreachable!("should not execute if parsing succeeded. QED."),
|
Instruction::Error => unreachable!("should not execute if parsing succeeded. QED."),
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
mod completeness_updater;
|
mod completeness_updater;
|
||||||
|
|
||||||
use super::Catchable;
|
|
||||||
use super::ExecutableInstruction;
|
use super::ExecutableInstruction;
|
||||||
use super::ExecutionCtx;
|
use super::ExecutionCtx;
|
||||||
use super::ExecutionError;
|
use super::ExecutionError;
|
||||||
@ -30,8 +29,6 @@ use completeness_updater::ParCompletenessUpdater;
|
|||||||
use air_parser::ast::Par;
|
use air_parser::ast::Par;
|
||||||
use air_trace_handler::SubtreeType;
|
use air_trace_handler::SubtreeType;
|
||||||
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
impl<'i> ExecutableInstruction<'i> for Par<'i> {
|
impl<'i> ExecutableInstruction<'i> for Par<'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<()> {
|
||||||
@ -84,7 +81,7 @@ fn execute_subtree<'i>(
|
|||||||
|
|
||||||
enum SubtreeResult {
|
enum SubtreeResult {
|
||||||
Succeeded,
|
Succeeded,
|
||||||
Failed(Rc<ExecutionError>),
|
Failed(ExecutionError),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_par_result(
|
fn prepare_par_result(
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::Catchable;
|
|
||||||
use super::ExecutionCtx;
|
use super::ExecutionCtx;
|
||||||
use super::ExecutionError;
|
use super::ExecutionError;
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
@ -42,12 +41,13 @@ impl<'i> super::ExecutableInstruction<'i> for Xor<'i> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_xor_log(e: &ExecutionError) {
|
fn print_xor_log(e: &ExecutionError) {
|
||||||
match e {
|
if e.is_match_or_mismatch() {
|
||||||
// These errors actually aren't real errors, but a way to bubble execution_step up from match
|
// 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
|
// 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
|
// bubble up until the very beginning of current subtree. So the error message shouldn't
|
||||||
// be print out in order not to confuse users.
|
// be print out in order not to confuse users.
|
||||||
ExecutionError::MatchWithoutXorError | ExecutionError::MismatchWithoutXorError => {}
|
return;
|
||||||
e => log::warn!("xor caught an error: {}", e),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log::warn!("xor caught an error: {}", e);
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ mod resolved_call_result;
|
|||||||
mod stream;
|
mod stream;
|
||||||
|
|
||||||
use super::iterable::IterableItem;
|
use super::iterable::IterableItem;
|
||||||
use super::ExecutionError;
|
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::ValueAggregate;
|
use super::ValueAggregate;
|
||||||
use crate::execution_step::lambda_applier::*;
|
use crate::execution_step::lambda_applier::*;
|
||||||
|
@ -14,12 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::ExecutionError::LambdaApplierError;
|
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::JValuable;
|
use super::JValuable;
|
||||||
use super::LambdaAST;
|
use super::LambdaAST;
|
||||||
use super::LambdaError::EmptyStream;
|
use super::LambdaError::EmptyStream;
|
||||||
use crate::exec_err;
|
use crate::execution_step::CatchableError::LambdaApplierError;
|
||||||
use crate::execution_step::ExecutionCtx;
|
use crate::execution_step::ExecutionCtx;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
use crate::execution_step::SecurityTetraplets;
|
use crate::execution_step::SecurityTetraplets;
|
||||||
@ -30,7 +29,7 @@ use std::borrow::Cow;
|
|||||||
impl JValuable for () {
|
impl JValuable for () {
|
||||||
fn apply_lambda<'i>(&self, _lambda: &LambdaAST<'_>, _exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
fn apply_lambda<'i>(&self, _lambda: &LambdaAST<'_>, _exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||||
// applying lambda to an empty stream will produce a join behaviour
|
// applying lambda to an empty stream will produce a join behaviour
|
||||||
exec_err!(LambdaApplierError(EmptyStream))
|
Err(LambdaApplierError(EmptyStream).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_lambda_with_tetraplets<'i>(
|
fn apply_lambda_with_tetraplets<'i>(
|
||||||
@ -39,7 +38,7 @@ impl JValuable for () {
|
|||||||
_exec_ctx: &ExecutionCtx<'i>,
|
_exec_ctx: &ExecutionCtx<'i>,
|
||||||
) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
||||||
// applying lambda to an empty stream will produce a join behaviour
|
// applying lambda to an empty stream will produce a join behaviour
|
||||||
exec_err!(LambdaApplierError(EmptyStream))
|
Err(LambdaApplierError(EmptyStream).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_jvalue(&self) -> Cow<'_, JValue> {
|
fn as_jvalue(&self) -> Cow<'_, JValue> {
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
use super::select_from_stream;
|
use super::select_from_stream;
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::JValuable;
|
use super::JValuable;
|
||||||
use crate::exec_err;
|
|
||||||
use crate::execution_step::boxed_value::Generation;
|
use crate::execution_step::boxed_value::Generation;
|
||||||
use crate::execution_step::boxed_value::Stream;
|
use crate::execution_step::boxed_value::Stream;
|
||||||
use crate::execution_step::ExecutionCtx;
|
use crate::execution_step::ExecutionCtx;
|
||||||
@ -89,7 +88,7 @@ impl<'stream> StreamJvaluableIngredients<'stream> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(self) fn iter(&self) -> ExecutionResult<StreamIter<'_>> {
|
pub(self) fn iter(&self) -> ExecutionResult<StreamIter<'_>> {
|
||||||
use super::ExecutionError::StreamDontHaveSuchGeneration;
|
use crate::execution_step::CatchableError::StreamDontHaveSuchGeneration;
|
||||||
|
|
||||||
match self.stream.iter(self.generation) {
|
match self.stream.iter(self.generation) {
|
||||||
Some(iter) => Ok(iter),
|
Some(iter) => Ok(iter),
|
||||||
@ -99,10 +98,7 @@ impl<'stream> StreamJvaluableIngredients<'stream> {
|
|||||||
Generation::Last => unreachable!(),
|
Generation::Last => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
exec_err!(StreamDontHaveSuchGeneration(
|
Err(StreamDontHaveSuchGeneration(self.stream.deref().clone(), generation as usize).into())
|
||||||
self.stream.deref().clone(),
|
|
||||||
generation as usize
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ mod scalar;
|
|||||||
mod stream;
|
mod stream;
|
||||||
mod variable;
|
mod variable;
|
||||||
|
|
||||||
pub(crate) use super::ExecutionError;
|
|
||||||
pub(crate) use iterable::*;
|
pub(crate) use iterable::*;
|
||||||
pub(crate) use jvaluable::*;
|
pub(crate) use jvaluable::*;
|
||||||
pub(crate) use scalar::ScalarRef;
|
pub(crate) use scalar::ScalarRef;
|
||||||
|
@ -14,10 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::ExecutionError;
|
|
||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::ValueAggregate;
|
use super::ValueAggregate;
|
||||||
use crate::exec_err;
|
use crate::execution_step::CatchableError;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
|
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
@ -29,9 +28,8 @@ use std::fmt::Formatter;
|
|||||||
/// of values that interpreter obtained from one particle. It means that number of generation on
|
/// 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
|
/// 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.
|
/// 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)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub(crate) struct Stream(Vec<Vec<ValueAggregate>>);
|
pub struct Stream(Vec<Vec<ValueAggregate>>);
|
||||||
|
|
||||||
impl Stream {
|
impl Stream {
|
||||||
pub(crate) fn from_generations_count(count: usize) -> Self {
|
pub(crate) fn from_generations_count(count: usize) -> Self {
|
||||||
@ -51,7 +49,7 @@ impl Stream {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if generation >= self.0.len() {
|
if generation >= self.0.len() {
|
||||||
return exec_err!(ExecutionError::StreamDontHaveSuchGeneration(self.clone(), generation));
|
return Err(CatchableError::StreamDontHaveSuchGeneration(self.clone(), generation).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.0[generation].push(value);
|
self.0[generation].push(value);
|
||||||
|
@ -14,19 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mod catchable;
|
pub(crate) use super::Joinable;
|
||||||
mod joinable;
|
|
||||||
|
|
||||||
pub(crate) use catchable::Catchable;
|
|
||||||
pub(crate) use joinable::Joinable;
|
|
||||||
|
|
||||||
use super::Stream;
|
use super::Stream;
|
||||||
use crate::execution_step::lambda_applier::LambdaError;
|
use crate::execution_step::lambda_applier::LambdaError;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
use crate::ToErrorCode;
|
use crate::ToErrorCode;
|
||||||
|
|
||||||
use air_trace_handler::MergerApResult;
|
|
||||||
use air_trace_handler::TraceHandlerError;
|
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use strum_macros::EnumDiscriminants;
|
use strum_macros::EnumDiscriminants;
|
||||||
use strum_macros::EnumIter;
|
use strum_macros::EnumIter;
|
||||||
@ -34,26 +28,22 @@ use thiserror::Error as ThisError;
|
|||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Errors arisen while executing AIR script.
|
/// Catchable errors arisen during AIR script execution. Catchable here means that these errors
|
||||||
|
/// could be handled by a xor instruction and their error_code could be used in a match
|
||||||
|
/// instruction.
|
||||||
#[derive(ThisError, EnumDiscriminants, Debug)]
|
#[derive(ThisError, EnumDiscriminants, Debug)]
|
||||||
#[strum_discriminants(derive(EnumIter))]
|
#[strum_discriminants(derive(EnumIter))]
|
||||||
pub(crate) enum ExecutionError {
|
pub enum CatchableError {
|
||||||
/// An error is occurred while calling local service via call_service.
|
/// An error is occurred while calling local service via call_service.
|
||||||
#[error("Local service error, ret_code is {0}, error message is '{1}'")]
|
#[error("Local service error, ret_code is {0}, error message is '{1}'")]
|
||||||
LocalServiceError(i32, Rc<String>),
|
LocalServiceError(i32, Rc<String>),
|
||||||
|
|
||||||
/// Variable with such a name wasn't defined during AIR script execution.
|
/// Variable with such a name wasn't defined during AIR script execution.
|
||||||
|
/// This error type is used in order to support the join behaviour and
|
||||||
|
/// it's ok if some variable hasn't been defined yet, due to the par nature of AIR.
|
||||||
#[error("variable with name '{0}' wasn't defined during script execution")]
|
#[error("variable with name '{0}' wasn't defined during script execution")]
|
||||||
VariableNotFound(String),
|
VariableNotFound(String),
|
||||||
|
|
||||||
/// Multiple values for such name found.
|
|
||||||
#[error("multiple variables found for name '{0}' in data")]
|
|
||||||
MultipleVariablesFound(String),
|
|
||||||
|
|
||||||
/// An error occurred while trying to apply lambda to a value.
|
|
||||||
#[error(transparent)]
|
|
||||||
LambdaApplierError(#[from] LambdaError),
|
|
||||||
|
|
||||||
/// Provided JValue has incompatible type with a requested one.
|
/// Provided JValue has incompatible type with a requested one.
|
||||||
#[error(
|
#[error(
|
||||||
"expected JValue type '{expected_value_type}' for the variable `{variable_name}`, but got '{actual_value}'"
|
"expected JValue type '{expected_value_type}' for the variable `{variable_name}`, but got '{actual_value}'"
|
||||||
@ -64,22 +54,10 @@ pub(crate) enum ExecutionError {
|
|||||||
expected_value_type: &'static str,
|
expected_value_type: &'static str,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Fold state wasn't found for such iterator name.
|
|
||||||
#[error("fold state not found for this iterable '{0}'")]
|
|
||||||
FoldStateNotFound(String),
|
|
||||||
|
|
||||||
/// Multiple fold states found for such iterator name.
|
|
||||||
#[error("multiple iterable values found for iterable name '{0}'")]
|
|
||||||
MultipleIterableValues(String),
|
|
||||||
|
|
||||||
/// A fold instruction must iterate over array value.
|
/// A fold instruction must iterate over array value.
|
||||||
#[error("lambda '{1}' returned non-array value '{0}' for fold instruction")]
|
#[error("lambda '{1}' returned non-array value '{0}' for fold instruction")]
|
||||||
FoldIteratesOverNonArray(JValue, String),
|
FoldIteratesOverNonArray(JValue, String),
|
||||||
|
|
||||||
/// Errors encountered while shadowing non-scalar values.
|
|
||||||
#[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.
|
/// 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("match is used without corresponding xor")]
|
||||||
MatchWithoutXorError,
|
MatchWithoutXorError,
|
||||||
@ -92,61 +70,31 @@ pub(crate) enum ExecutionError {
|
|||||||
#[error("fail with ret_code '{ret_code}' and error_message '{error_message}' is used without corresponding xor")]
|
#[error("fail with ret_code '{ret_code}' and error_message '{error_message}' is used without corresponding xor")]
|
||||||
FailWithoutXorError { ret_code: i64, error_message: String },
|
FailWithoutXorError { ret_code: i64, error_message: String },
|
||||||
|
|
||||||
/// Errors bubbled from a trace handler.
|
/// An error occurred while trying to apply lambda to a value.
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TraceError(#[from] TraceHandlerError),
|
LambdaApplierError(#[from] LambdaError),
|
||||||
|
|
||||||
/// Errors occurred while insertion of a value inside stream that doesn't have corresponding generation.
|
/// 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")]
|
#[error("stream {0:?} doesn't have generation with number {1}, probably a supplied to the interpreter data is corrupted")]
|
||||||
StreamDontHaveSuchGeneration(Stream, usize),
|
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<LambdaError> for Rc<ExecutionError> {
|
impl From<LambdaError> for Rc<CatchableError> {
|
||||||
fn from(e: LambdaError) -> Self {
|
fn from(e: LambdaError) -> Self {
|
||||||
Rc::new(ExecutionError::LambdaApplierError(e))
|
Rc::new(CatchableError::LambdaApplierError(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This macro is needed because it's impossible to implement
|
impl ToErrorCode for Rc<CatchableError> {
|
||||||
/// From<TraceHandlerError> for Rc<ExecutionError> due to the orphan rule.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! trace_to_exec_err {
|
|
||||||
($trace_expr: expr) => {
|
|
||||||
$trace_expr.map_err(|e| std::rc::Rc::new(crate::execution_step::ExecutionError::TraceError(e)))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
impl ToErrorCode for ExecutionError {
|
|
||||||
fn to_error_code(&self) -> i64 {
|
fn to_error_code(&self) -> i64 {
|
||||||
const EXECUTION_ERRORS_START_ID: i64 = 1000;
|
self.as_ref().to_error_code()
|
||||||
|
|
||||||
let mut errors = ExecutionErrorDiscriminants::iter();
|
|
||||||
let actual_error_type = ExecutionErrorDiscriminants::from(self);
|
|
||||||
|
|
||||||
// unwrap is safe here because errors are guaranteed to contain all errors variants
|
|
||||||
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
|
|
||||||
EXECUTION_ERRORS_START_ID + enum_variant_position
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
impl ToErrorCode for CatchableError {
|
||||||
|
|
||||||
impl ToErrorCode for Rc<ExecutionError> {
|
|
||||||
fn to_error_code(&self) -> i64 {
|
fn to_error_code(&self) -> i64 {
|
||||||
const EXECUTION_ERRORS_START_ID: i64 = 1000;
|
use crate::utils::CATCHABLE_ERRORS_START_ID;
|
||||||
|
crate::generate_to_error_code!(self, CatchableError, CATCHABLE_ERRORS_START_ID)
|
||||||
let mut errors = ExecutionErrorDiscriminants::iter();
|
|
||||||
let actual_error_type = ExecutionErrorDiscriminants::from(self.as_ref());
|
|
||||||
|
|
||||||
// unwrap is safe here because errors are guaranteed to contain all errors variants
|
|
||||||
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
|
|
||||||
EXECUTION_ERRORS_START_ID + enum_variant_position
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,11 +105,11 @@ macro_rules! log_join {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rustfmt::skip::macros(log_join)]
|
#[rustfmt::skip::macros(log_join)]
|
||||||
impl Joinable for ExecutionError {
|
impl Joinable for CatchableError {
|
||||||
/// Returns true, if supplied error is related to variable not found errors type.
|
/// Returns true, if supplied error is related to variable not found errors type.
|
||||||
/// Print log if this is joinable error type.
|
/// Print log if this is joinable error type.
|
||||||
fn is_joinable(&self) -> bool {
|
fn is_joinable(&self) -> bool {
|
||||||
use ExecutionError::*;
|
use CatchableError::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
VariableNotFound(var_name) => {
|
VariableNotFound(var_name) => {
|
||||||
@ -181,16 +129,3 @@ impl Joinable for ExecutionError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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!()
|
|
||||||
}
|
|
||||||
}
|
|
87
air/src/execution_step/errors/execution_errors.rs
Normal file
87
air/src/execution_step/errors/execution_errors.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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::CatchableError;
|
||||||
|
use super::Joinable;
|
||||||
|
use super::UncatchableError;
|
||||||
|
use crate::ToErrorCode;
|
||||||
|
|
||||||
|
use strum_macros::EnumDiscriminants;
|
||||||
|
use strum_macros::EnumIter;
|
||||||
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
// TODO: add tests for all execution errors
|
||||||
|
/// Errors arisen while executing AIR script.
|
||||||
|
/// This enum is pub since it's used in tests.
|
||||||
|
#[derive(ThisError, EnumDiscriminants, Debug)]
|
||||||
|
#[strum_discriminants(derive(EnumIter))]
|
||||||
|
pub enum ExecutionError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Catchable(#[from] Rc<CatchableError>),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Uncatchable(#[from] UncatchableError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecutionError {
|
||||||
|
pub fn is_catchable(&self) -> bool {
|
||||||
|
matches!(self, ExecutionError::Catchable(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_match_or_mismatch(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ExecutionError::Catchable(catchable) => matches!(
|
||||||
|
catchable.as_ref(),
|
||||||
|
CatchableError::MatchWithoutXorError | CatchableError::MismatchWithoutXorError
|
||||||
|
),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CatchableError> for ExecutionError {
|
||||||
|
fn from(catchable: CatchableError) -> Self {
|
||||||
|
Self::Catchable(std::rc::Rc::new(catchable))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! trace_to_exec_err {
|
||||||
|
($trace_expr: expr) => {
|
||||||
|
$trace_expr.map_err(|e| {
|
||||||
|
crate::execution_step::ExecutionError::Uncatchable(crate::execution_step::UncatchableError::TraceError(e))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
impl ToErrorCode for ExecutionError {
|
||||||
|
fn to_error_code(&self) -> i64 {
|
||||||
|
match self {
|
||||||
|
ExecutionError::Catchable(err) => err.to_error_code(),
|
||||||
|
ExecutionError::Uncatchable(err) => err.to_error_code(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Joinable for ExecutionError {
|
||||||
|
fn is_joinable(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ExecutionError::Catchable(err) => err.is_joinable(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,11 +14,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// This trait is intended to differentiate between catchable and non-catchable error types.
|
mod catchable_errors;
|
||||||
/// Errors of the first type could be caught by xor, the second couldn't and should stop
|
mod execution_errors;
|
||||||
/// AIR execution. This is needed to prevent some malicious data merging and manage
|
mod joinable;
|
||||||
/// prev_data always in a valid state.
|
mod uncatchable_errors;
|
||||||
pub(crate) trait Catchable {
|
|
||||||
/// Return true, if error is catchable.
|
pub use catchable_errors::CatchableError;
|
||||||
fn is_catchable(&self) -> bool;
|
pub use execution_errors::ExecutionError;
|
||||||
}
|
pub use uncatchable_errors::UncatchableError;
|
||||||
|
|
||||||
|
pub(crate) use joinable::Joinable;
|
||||||
|
|
||||||
|
use super::Stream;
|
65
air/src/execution_step/errors/uncatchable_errors.rs
Normal file
65
air/src/execution_step/errors/uncatchable_errors.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::ToErrorCode;
|
||||||
|
|
||||||
|
use air_trace_handler::MergerApResult;
|
||||||
|
use air_trace_handler::TraceHandlerError;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
use strum_macros::EnumDiscriminants;
|
||||||
|
use strum_macros::EnumIter;
|
||||||
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
|
/// Uncatchable errors arisen during AIR script execution. Uncatchable here means that these errors
|
||||||
|
/// couldn't be handled by a xor instruction and their error_code couldn't be used in a match
|
||||||
|
/// instruction. They are similar to JVM runtime errors and some of them could be caught only
|
||||||
|
/// while execution of AIR script, others (FoldStateNotFound and MultipleVariablesFound) are
|
||||||
|
/// checked additionally on the validation step, and presence here for convenience.
|
||||||
|
#[derive(ThisError, EnumDiscriminants, Debug)]
|
||||||
|
#[strum_discriminants(derive(EnumIter))]
|
||||||
|
pub enum UncatchableError {
|
||||||
|
/// Errors bubbled from a trace handler.
|
||||||
|
#[error(transparent)]
|
||||||
|
TraceError(#[from] TraceHandlerError),
|
||||||
|
|
||||||
|
/// Fold state wasn't found for such iterator name.
|
||||||
|
#[error("fold state not found for this iterable '{0}'")]
|
||||||
|
FoldStateNotFound(String),
|
||||||
|
|
||||||
|
/// Errors encountered while shadowing non-scalar values.
|
||||||
|
#[error("variable with name '{0}' can't be shadowed, shadowing isn't supported for iterables")]
|
||||||
|
IterableShadowing(String),
|
||||||
|
|
||||||
|
/// Multiple fold states found for such iterator name.
|
||||||
|
#[error("multiple iterable values found for iterable name '{0}'")]
|
||||||
|
MultipleIterableValues(String),
|
||||||
|
|
||||||
|
/// 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),
|
||||||
|
|
||||||
|
/// Multiple values for such name found.
|
||||||
|
#[error("multiple variables found for name '{0}' in data")]
|
||||||
|
MultipleVariablesFound(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToErrorCode for UncatchableError {
|
||||||
|
fn to_error_code(&self) -> i64 {
|
||||||
|
use crate::utils::UNCATCHABLE_ERRORS_START_ID;
|
||||||
|
crate::generate_to_error_code!(self, UncatchableError, UNCATCHABLE_ERRORS_START_ID)
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::ExecutionCtx;
|
use super::ExecutionCtx;
|
||||||
use crate::execution_step::ExecutionError;
|
use crate::execution_step::CatchableError;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
use crate::SecurityTetraplet;
|
use crate::SecurityTetraplet;
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ use std::rc::Rc;
|
|||||||
/// This struct is intended to track the last arisen error.
|
/// This struct is intended to track the last arisen error.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct LastErrorDescriptor {
|
pub(crate) struct LastErrorDescriptor {
|
||||||
pub(crate) error: Rc<ExecutionError>,
|
pub(crate) error: Rc<CatchableError>,
|
||||||
pub(crate) instruction: String,
|
pub(crate) instruction: String,
|
||||||
pub(crate) peer_id: String,
|
pub(crate) peer_id: String,
|
||||||
pub(crate) tetraplet: Option<RSecurityTetraplet>,
|
pub(crate) tetraplet: Option<RSecurityTetraplet>,
|
||||||
@ -70,7 +70,7 @@ impl<'s> LastErrorWithTetraplet {
|
|||||||
|
|
||||||
impl LastErrorDescriptor {
|
impl LastErrorDescriptor {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
error: Rc<ExecutionError>,
|
error: Rc<CatchableError>,
|
||||||
instruction: String,
|
instruction: String,
|
||||||
peer_id: String,
|
peer_id: String,
|
||||||
tetraplet: Option<RSecurityTetraplet>,
|
tetraplet: Option<RSecurityTetraplet>,
|
||||||
|
@ -14,9 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::exec_err;
|
|
||||||
use crate::execution_step::boxed_value::ScalarRef;
|
use crate::execution_step::boxed_value::ScalarRef;
|
||||||
use crate::execution_step::ExecutionError;
|
use crate::execution_step::errors_prelude::*;
|
||||||
use crate::execution_step::ExecutionResult;
|
use crate::execution_step::ExecutionResult;
|
||||||
use crate::execution_step::FoldState;
|
use crate::execution_step::FoldState;
|
||||||
use crate::execution_step::ValueAggregate;
|
use crate::execution_step::ValueAggregate;
|
||||||
@ -87,7 +86,7 @@ impl<'i> Scalars<'i> {
|
|||||||
}
|
}
|
||||||
Occupied(entry) => {
|
Occupied(entry) => {
|
||||||
if !shadowing_allowed {
|
if !shadowing_allowed {
|
||||||
return exec_err!(ExecutionError::MultipleVariablesFound(entry.key().clone()));
|
return Err(UncatchableError::MultipleVariablesFound(entry.key().clone()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let values = entry.into_mut();
|
let values = entry.into_mut();
|
||||||
@ -115,9 +114,7 @@ impl<'i> Scalars<'i> {
|
|||||||
entry.insert(fold_state);
|
entry.insert(fold_state);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Occupied(entry) => {
|
Occupied(entry) => Err(UncatchableError::MultipleIterableValues(entry.key().clone()).into()),
|
||||||
exec_err!(ExecutionError::MultipleIterableValues(entry.key().clone()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,13 +132,13 @@ impl<'i> Scalars<'i> {
|
|||||||
.rev()
|
.rev()
|
||||||
.find_map(|scalar| scalar.as_ref())
|
.find_map(|scalar| scalar.as_ref())
|
||||||
})
|
})
|
||||||
.ok_or_else(|| Rc::new(ExecutionError::VariableNotFound(name.to_string())))
|
.ok_or_else(|| Rc::new(CatchableError::VariableNotFound(name.to_string())).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_iterable_mut(&mut self, name: &str) -> ExecutionResult<&mut FoldState<'i>> {
|
pub(crate) fn get_iterable_mut(&mut self, name: &str) -> ExecutionResult<&mut FoldState<'i>> {
|
||||||
self.iterable_values
|
self.iterable_values
|
||||||
.get_mut(name)
|
.get_mut(name)
|
||||||
.ok_or_else(|| Rc::new(ExecutionError::FoldStateNotFound(name.to_string())))
|
.ok_or_else(|| UncatchableError::FoldStateNotFound(name.to_string()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get(&'i self, name: &str) -> ExecutionResult<ScalarRef<'i>> {
|
pub(crate) fn get(&'i self, name: &str) -> ExecutionResult<ScalarRef<'i>> {
|
||||||
@ -149,7 +146,7 @@ impl<'i> Scalars<'i> {
|
|||||||
let iterable_value = self.iterable_values.get(name);
|
let iterable_value = self.iterable_values.get(name);
|
||||||
|
|
||||||
match (value, iterable_value) {
|
match (value, iterable_value) {
|
||||||
(Err(_), None) => exec_err!(ExecutionError::VariableNotFound(name.to_string())),
|
(Err(_), None) => Err(CatchableError::VariableNotFound(name.to_string()).into()),
|
||||||
(Ok(value), None) => Ok(ScalarRef::Value(value)),
|
(Ok(value), None) => Ok(ScalarRef::Value(value)),
|
||||||
(Err(_), Some(iterable_value)) => Ok(ScalarRef::IterableValue(iterable_value)),
|
(Err(_), Some(iterable_value)) => Ok(ScalarRef::IterableValue(iterable_value)),
|
||||||
(Ok(_), Some(_)) => unreachable!("this is checked on the parsing stage"),
|
(Ok(_), Some(_)) => unreachable!("this is checked on the parsing stage"),
|
||||||
|
@ -189,7 +189,7 @@ fn find_closest<'d>(
|
|||||||
) -> Option<&'d RefCell<Stream>> {
|
) -> Option<&'d RefCell<Stream>> {
|
||||||
// descriptors are placed in a order of decreasing scopes, so it's enough to get the latest suitable
|
// descriptors are placed in a order of decreasing scopes, so it's enough to get the latest suitable
|
||||||
for descriptor in descriptors.rev() {
|
for descriptor in descriptors.rev() {
|
||||||
if descriptor.span.contains(position) {
|
if descriptor.span.contains_position(position) {
|
||||||
return Some(&descriptor.stream);
|
return Some(&descriptor.stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ use super::utils::*;
|
|||||||
use super::LambdaError;
|
use super::LambdaError;
|
||||||
use crate::execution_step::ExecutionCtx;
|
use crate::execution_step::ExecutionCtx;
|
||||||
use crate::execution_step::ExecutionResult;
|
use crate::execution_step::ExecutionResult;
|
||||||
|
use crate::lambda_to_execution_error;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
use crate::LambdaAST;
|
use crate::LambdaAST;
|
||||||
|
|
||||||
@ -39,19 +40,18 @@ pub(crate) fn select_from_stream<'value, 'i>(
|
|||||||
let idx = match prefix {
|
let idx = match prefix {
|
||||||
ArrayAccess { idx } => *idx,
|
ArrayAccess { idx } => *idx,
|
||||||
FieldAccessByName { field_name } => {
|
FieldAccessByName { field_name } => {
|
||||||
return Err(LambdaError::FieldAccessorAppliedToStream {
|
return lambda_to_execution_error!(Err(LambdaError::FieldAccessorAppliedToStream {
|
||||||
field_name: field_name.to_string(),
|
field_name: field_name.to_string(),
|
||||||
}
|
}));
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
_ => unreachable!("should not execute if parsing succeeded. QED."),
|
_ => unreachable!("should not execute if parsing succeeded. QED."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let stream_size = stream.len();
|
let stream_size = stream.len();
|
||||||
let value = stream
|
let value = lambda_to_execution_error!(stream
|
||||||
.peekable()
|
.peekable()
|
||||||
.nth(idx as usize)
|
.nth(idx as usize)
|
||||||
.ok_or(LambdaError::StreamNotHaveEnoughValues { stream_size, idx })?;
|
.ok_or(LambdaError::StreamNotHaveEnoughValues { stream_size, idx }))?;
|
||||||
|
|
||||||
let result = select(value, body.iter(), exec_ctx)?;
|
let result = select(value, body.iter(), exec_ctx)?;
|
||||||
let select_result = StreamSelectResult::new(result, idx);
|
let select_result = StreamSelectResult::new(result, idx);
|
||||||
@ -66,14 +66,14 @@ pub(crate) fn select<'value, 'accessor, 'i>(
|
|||||||
for accessor in lambda {
|
for accessor in lambda {
|
||||||
match accessor {
|
match accessor {
|
||||||
ValueAccessor::ArrayAccess { idx } => {
|
ValueAccessor::ArrayAccess { idx } => {
|
||||||
value = try_jvalue_with_idx(value, *idx)?;
|
value = lambda_to_execution_error!(try_jvalue_with_idx(value, *idx))?;
|
||||||
}
|
}
|
||||||
ValueAccessor::FieldAccessByName { field_name } => {
|
ValueAccessor::FieldAccessByName { field_name } => {
|
||||||
value = try_jvalue_with_field_name(value, *field_name)?;
|
value = lambda_to_execution_error!(try_jvalue_with_field_name(value, *field_name))?;
|
||||||
}
|
}
|
||||||
ValueAccessor::FieldAccessByScalar { scalar_name } => {
|
ValueAccessor::FieldAccessByScalar { scalar_name } => {
|
||||||
let scalar = exec_ctx.scalars.get(scalar_name)?;
|
let scalar = exec_ctx.scalars.get(scalar_name)?;
|
||||||
value = select_by_scalar(value, scalar)?;
|
value = lambda_to_execution_error!(select_by_scalar(value, scalar))?;
|
||||||
}
|
}
|
||||||
ValueAccessor::Error => unreachable!("should not execute if parsing succeeded. QED."),
|
ValueAccessor::Error => unreachable!("should not execute if parsing succeeded. QED."),
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,9 @@ use crate::JValue;
|
|||||||
|
|
||||||
use thiserror::Error as ThisError;
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
|
/// Describes errors related to applying lambdas to values.
|
||||||
#[derive(Debug, Clone, ThisError)]
|
#[derive(Debug, Clone, ThisError)]
|
||||||
pub(crate) enum LambdaError {
|
pub enum LambdaError {
|
||||||
#[error("lambda is applied to a stream that have only '{stream_size}' elements, but '{idx}' requested")]
|
#[error("lambda is applied to a stream that have only '{stream_size}' elements, but '{idx}' requested")]
|
||||||
StreamNotHaveEnoughValues { stream_size: usize, idx: u32 },
|
StreamNotHaveEnoughValues { stream_size: usize, idx: u32 },
|
||||||
|
|
||||||
@ -36,12 +37,12 @@ pub(crate) enum LambdaError {
|
|||||||
#[error("value '{value}' does not contain element for idx = '{idx}'")]
|
#[error("value '{value}' does not contain element for idx = '{idx}'")]
|
||||||
ValueNotContainSuchArrayIdx { value: JValue, idx: u32 },
|
ValueNotContainSuchArrayIdx { value: JValue, idx: u32 },
|
||||||
|
|
||||||
|
#[error("value '{value}' does not contain element with field name = '{field_name}'")]
|
||||||
|
ValueNotContainSuchField { value: JValue, field_name: String },
|
||||||
|
|
||||||
#[error("value '{value}' is not an map-type to match field accessor with field_name = '{field_name}'")]
|
#[error("value '{value}' is not an map-type to match field accessor with field_name = '{field_name}'")]
|
||||||
FieldAccessorNotMatchValue { value: JValue, field_name: String },
|
FieldAccessorNotMatchValue { value: JValue, field_name: String },
|
||||||
|
|
||||||
#[error("value '{value}' does not contain element with field name = '{field_name}'")]
|
|
||||||
JValueNotContainSuchField { value: JValue, field_name: String },
|
|
||||||
|
|
||||||
#[error("index accessor `{accessor} can't be converted to u32`")]
|
#[error("index accessor `{accessor} can't be converted to u32`")]
|
||||||
IndexAccessNotU32 { accessor: serde_json::Number },
|
IndexAccessNotU32 { accessor: serde_json::Number },
|
||||||
|
|
||||||
|
@ -18,8 +18,20 @@ mod applier;
|
|||||||
mod errors;
|
mod errors;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
pub use errors::LambdaError;
|
||||||
|
|
||||||
pub(crate) type LambdaResult<T> = std::result::Result<T, LambdaError>;
|
pub(crate) type LambdaResult<T> = std::result::Result<T, LambdaError>;
|
||||||
|
|
||||||
pub(crate) use applier::select;
|
pub(crate) use applier::select;
|
||||||
pub(crate) use applier::select_from_stream;
|
pub(crate) use applier::select_from_stream;
|
||||||
pub(crate) use errors::LambdaError;
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! lambda_to_execution_error {
|
||||||
|
($lambda_expr: expr) => {
|
||||||
|
$lambda_expr.map_err(|lambda_error| {
|
||||||
|
crate::execution_step::ExecutionError::Catchable(std::rc::Rc::new(
|
||||||
|
crate::execution_step::CatchableError::LambdaApplierError(lambda_error),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -39,14 +39,12 @@ pub(super) fn try_jvalue_with_field_name<'value>(
|
|||||||
field_name: &str,
|
field_name: &str,
|
||||||
) -> LambdaResult<&'value JValue> {
|
) -> LambdaResult<&'value JValue> {
|
||||||
match jvalue {
|
match jvalue {
|
||||||
JValue::Object(values_map) => {
|
JValue::Object(values_map) => values_map
|
||||||
values_map
|
.get(field_name)
|
||||||
.get(field_name)
|
.ok_or_else(|| LambdaError::ValueNotContainSuchField {
|
||||||
.ok_or_else(|| LambdaError::JValueNotContainSuchField {
|
value: jvalue.clone(),
|
||||||
value: jvalue.clone(),
|
field_name: field_name.to_string(),
|
||||||
field_name: field_name.to_string(),
|
}),
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => Err(LambdaError::FieldAccessorNotMatchValue {
|
_ => Err(LambdaError::FieldAccessorNotMatchValue {
|
||||||
value: jvalue.clone(),
|
value: jvalue.clone(),
|
||||||
field_name: field_name.to_string(),
|
field_name: field_name.to_string(),
|
||||||
|
@ -21,14 +21,23 @@ pub(crate) mod execution_context;
|
|||||||
mod lambda_applier;
|
mod lambda_applier;
|
||||||
mod resolver;
|
mod resolver;
|
||||||
|
|
||||||
|
pub use errors::CatchableError;
|
||||||
|
pub use errors::ExecutionError;
|
||||||
|
pub use errors::UncatchableError;
|
||||||
|
pub use lambda_applier::LambdaError;
|
||||||
|
|
||||||
|
pub mod errors_prelude {
|
||||||
|
pub use super::CatchableError;
|
||||||
|
pub use super::ExecutionError;
|
||||||
|
pub use super::UncatchableError;
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) use self::air::ExecutableInstruction;
|
pub(super) use self::air::ExecutableInstruction;
|
||||||
pub(super) use self::air::FoldState;
|
pub(super) use self::air::FoldState;
|
||||||
pub(super) use boxed_value::Generation;
|
pub(super) use boxed_value::Generation;
|
||||||
pub(super) use boxed_value::ScalarRef;
|
pub(super) use boxed_value::ScalarRef;
|
||||||
pub(super) use boxed_value::Stream;
|
pub(super) use boxed_value::Stream;
|
||||||
pub(super) use boxed_value::ValueAggregate;
|
pub(super) use boxed_value::ValueAggregate;
|
||||||
pub(crate) use errors::Catchable;
|
|
||||||
pub(super) use errors::ExecutionError;
|
|
||||||
pub(crate) use errors::Joinable;
|
pub(crate) use errors::Joinable;
|
||||||
pub(crate) use execution_context::ExecutionCtx;
|
pub(crate) use execution_context::ExecutionCtx;
|
||||||
|
|
||||||
@ -37,13 +46,6 @@ pub(crate) use air_trace_handler::TraceHandler;
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
type ExecutionResult<T> = std::result::Result<T, Rc<ExecutionError>>;
|
type ExecutionResult<T> = std::result::Result<T, ExecutionError>;
|
||||||
type RSecurityTetraplet = Rc<RefCell<crate::SecurityTetraplet>>;
|
type RSecurityTetraplet = Rc<RefCell<crate::SecurityTetraplet>>;
|
||||||
type SecurityTetraplets = Vec<RSecurityTetraplet>;
|
type SecurityTetraplets = Vec<RSecurityTetraplet>;
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! exec_err {
|
|
||||||
($err:expr) => {
|
|
||||||
Err(std::rc::Rc::new($err))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
@ -35,13 +35,7 @@ pub enum FarewellError {
|
|||||||
|
|
||||||
impl ToErrorCode for FarewellError {
|
impl ToErrorCode for FarewellError {
|
||||||
fn to_error_code(&self) -> i64 {
|
fn to_error_code(&self) -> i64 {
|
||||||
const FAREWELL_ERRORS_START_ID: i64 = 20000;
|
use crate::utils::FAREWELL_ERRORS_START_ID;
|
||||||
|
crate::generate_to_error_code!(self, FarewellError, FAREWELL_ERRORS_START_ID)
|
||||||
let mut errors = FarewellErrorDiscriminants::iter();
|
|
||||||
let actual_error_type = FarewellErrorDiscriminants::from(self);
|
|
||||||
|
|
||||||
// unwrap is safe here because errors are guaranteed to contain all errors variants
|
|
||||||
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
|
|
||||||
FAREWELL_ERRORS_START_ID + enum_variant_position
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
mod errors;
|
mod errors;
|
||||||
mod outcome;
|
mod outcome;
|
||||||
|
|
||||||
pub(crate) use errors::FarewellError;
|
pub use errors::FarewellError;
|
||||||
|
|
||||||
pub(crate) use outcome::from_execution_error;
|
pub(crate) use outcome::from_execution_error;
|
||||||
pub(crate) use outcome::from_success_result;
|
pub(crate) use outcome::from_success_result;
|
||||||
pub(crate) use outcome::from_uncatchable_error;
|
pub(crate) use outcome::from_uncatchable_error;
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#![allow(improper_ctypes)]
|
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![deny(
|
#![deny(
|
||||||
dead_code,
|
dead_code,
|
||||||
@ -36,8 +35,14 @@ pub use air_interpreter_interface::InterpreterOutcome;
|
|||||||
pub use air_interpreter_interface::RunParameters;
|
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::LastError;
|
pub use execution_step::execution_context::LastError;
|
||||||
|
pub use execution_step::CatchableError;
|
||||||
|
pub use execution_step::ExecutionError;
|
||||||
|
pub use execution_step::LambdaError;
|
||||||
|
pub use execution_step::UncatchableError;
|
||||||
pub use polyplets::ResolvedTriplet;
|
pub use polyplets::ResolvedTriplet;
|
||||||
pub use polyplets::SecurityTetraplet;
|
pub use polyplets::SecurityTetraplet;
|
||||||
|
pub use preparation_step::PreparationError;
|
||||||
|
pub use utils::ToErrorCode;
|
||||||
|
|
||||||
pub use crate::runner::execute_air;
|
pub use crate::runner::execute_air;
|
||||||
|
|
||||||
@ -55,6 +60,5 @@ pub mod parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type JValue = serde_json::Value;
|
pub(crate) type JValue = serde_json::Value;
|
||||||
pub(crate) use utils::ToErrorCode;
|
|
||||||
|
|
||||||
use air_lambda_parser::LambdaAST;
|
use air_lambda_parser::LambdaAST;
|
||||||
|
@ -43,13 +43,7 @@ pub enum PreparationError {
|
|||||||
|
|
||||||
impl ToErrorCode for PreparationError {
|
impl ToErrorCode for PreparationError {
|
||||||
fn to_error_code(&self) -> i64 {
|
fn to_error_code(&self) -> i64 {
|
||||||
const PREPARATION_ERRORS_START_ID: i64 = 1;
|
use crate::utils::PREPARATION_ERROR_START_ID;
|
||||||
|
crate::generate_to_error_code!(self, PreparationError, PREPARATION_ERROR_START_ID)
|
||||||
let mut errors = PreparationErrorDiscriminants::iter();
|
|
||||||
let actual_error_type = PreparationErrorDiscriminants::from(self);
|
|
||||||
|
|
||||||
// unwrap is safe here because errors are guaranteed to contain all errors variants
|
|
||||||
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
|
|
||||||
PREPARATION_ERRORS_START_ID + enum_variant_position
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
mod errors;
|
mod errors;
|
||||||
mod preparation;
|
mod preparation;
|
||||||
|
|
||||||
pub(crate) use errors::PreparationError;
|
pub use errors::PreparationError;
|
||||||
|
|
||||||
pub(crate) use preparation::prepare;
|
pub(crate) use preparation::prepare;
|
||||||
pub(crate) use preparation::PreparationDescriptor;
|
pub(crate) use preparation::PreparationDescriptor;
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::execution_step::Catchable;
|
|
||||||
use crate::execution_step::ExecutableInstruction;
|
use crate::execution_step::ExecutableInstruction;
|
||||||
use crate::farewell_step as farewell;
|
use crate::farewell_step as farewell;
|
||||||
use crate::preparation_step::prepare;
|
use crate::preparation_step::prepare;
|
||||||
@ -58,8 +57,8 @@ fn execute_air_impl(
|
|||||||
mut trace_handler,
|
mut trace_handler,
|
||||||
air,
|
air,
|
||||||
} = match prepare(&prev_data, &data, air.as_str(), &call_results, params) {
|
} = match prepare(&prev_data, &data, air.as_str(), &call_results, params) {
|
||||||
Ok(desc) => desc,
|
Ok(descriptor) => descriptor,
|
||||||
// return the initial data in case of errors
|
// return the prev data in case of errors
|
||||||
Err(error) => return Err(farewell::from_uncatchable_error(prev_data, error)),
|
Err(error) => return Err(farewell::from_uncatchable_error(prev_data, error)),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,7 +68,7 @@ fn execute_air_impl(
|
|||||||
Ok(_) => farewell::from_success_result(exec_ctx, trace_handler),
|
Ok(_) => farewell::from_success_result(exec_ctx, trace_handler),
|
||||||
// return new collected trace in case of errors
|
// return new collected trace in case of errors
|
||||||
Err(error) if error.is_catchable() => Err(farewell::from_execution_error(exec_ctx, trace_handler, error)),
|
Err(error) if error.is_catchable() => Err(farewell::from_execution_error(exec_ctx, trace_handler, error)),
|
||||||
// return the old data in case of any trace errors
|
// return the prev data in case of any trace errors
|
||||||
Err(error) => Err(farewell::from_uncatchable_error(prev_data, error)),
|
Err(error) => Err(farewell::from_uncatchable_error(prev_data, error)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
air/src/utils/error_codes.rs
Normal file
21
air/src/utils/error_codes.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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 consts are used as start ids of corresponding errors.
|
||||||
|
pub(crate) const PREPARATION_ERROR_START_ID: i64 = 1;
|
||||||
|
pub(crate) const CATCHABLE_ERRORS_START_ID: i64 = 10000;
|
||||||
|
pub(crate) const UNCATCHABLE_ERRORS_START_ID: i64 = 20000;
|
||||||
|
pub(crate) const FAREWELL_ERRORS_START_ID: i64 = 30000;
|
@ -14,6 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
mod error_codes;
|
||||||
mod to_error_code;
|
mod to_error_code;
|
||||||
|
|
||||||
pub(crate) use to_error_code::ToErrorCode;
|
pub(crate) use error_codes::*;
|
||||||
|
pub use to_error_code::ToErrorCode;
|
||||||
|
@ -14,24 +14,26 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) trait ToErrorCode {
|
pub trait ToErrorCode {
|
||||||
fn to_error_code(&self) -> i64;
|
fn to_error_code(&self) -> i64;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
use concat_idents::concat_idents;
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! generate_to_error_code {
|
macro_rules! generate_to_error_code {
|
||||||
($error_type:ident, $start_id: ident) => {
|
($self: expr, $error_type:ident, $start_id: expr) => {
|
||||||
const PREPARATION_ERRORS_START_ID: u32 = $start_id;
|
concat_idents::concat_idents!(error_start_id = $error_type, _, START_ID {
|
||||||
|
concat_idents::concat_idents!(error_discriminant = $error_type, Discriminants { {
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
const error_start_id: i64 = $start_id;
|
||||||
|
|
||||||
let mut errors = PreparationErrorDiscriminants::iter();
|
let mut errors = error_discriminant::iter();
|
||||||
let actual_error_type = PreparationErrorDiscriminants::from(self);
|
let actual_error_type = error_discriminant::from($self);
|
||||||
|
|
||||||
// unwrap is safe here because errors are guaranteed to contain all errors variants
|
// unwrap is safe here because errors are guaranteed to contain all errors variants
|
||||||
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
|
let enum_variant_position = errors.position(|et| et == actual_error_type).unwrap() as i64;
|
||||||
PREPARATION_ERRORS_START_ID + enum_variant_position
|
error_start_id + enum_variant_position
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -14,8 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use air::UncatchableError;
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
|
use fstrings::f;
|
||||||
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
// Check that %init_peer_id% alias works correctly (by comparing result with it and explicit peer id).
|
// Check that %init_peer_id% alias works correctly (by comparing result with it and explicit peer id).
|
||||||
// Additionally, check that empty string for data does the same as empty call path.
|
// Additionally, check that empty string for data does the same as empty call path.
|
||||||
#[test]
|
#[test]
|
||||||
@ -98,18 +102,21 @@ fn variables() {
|
|||||||
// Check that duplicate variables are impossible.
|
// Check that duplicate variables are impossible.
|
||||||
#[test]
|
#[test]
|
||||||
fn duplicate_variables() {
|
fn duplicate_variables() {
|
||||||
let mut vm = create_avm(unit_call_service(), "some_peer_id");
|
let peer_id = "peer_id";
|
||||||
|
let mut vm = create_avm(unit_call_service(), peer_id);
|
||||||
|
|
||||||
let script = r#"
|
let variable_name = "modules";
|
||||||
|
let script = f!(r#"
|
||||||
(seq
|
(seq
|
||||||
(call "some_peer_id" ("some_service_id" "local_fn_name") [] modules)
|
(call "{peer_id}" ("some_service_id" "local_fn_name") [] {variable_name})
|
||||||
(call "some_peer_id" ("some_service_id" "local_fn_name") [] modules)
|
(call "{peer_id}" ("some_service_id" "local_fn_name") [] {variable_name})
|
||||||
)
|
)
|
||||||
"#;
|
"#);
|
||||||
|
|
||||||
let result = call_vm!(vm, "asd", script, "", "");
|
let result = call_vm!(vm, "asd", script, "", "");
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 1002);
|
let expected_error = UncatchableError::MultipleVariablesFound(variable_name.to_string());
|
||||||
|
assert!(check_error(&result, expected_error));
|
||||||
assert!(result.next_peer_pks.is_empty());
|
assert!(result.next_peer_pks.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,11 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use air::CatchableError;
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
use fstrings::f;
|
use fstrings::f;
|
||||||
use fstrings::format_args_f;
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fail_with_last_error() {
|
fn fail_with_last_error() {
|
||||||
let local_peer_id = "local_peer_id";
|
let local_peer_id = "local_peer_id";
|
||||||
@ -32,11 +35,10 @@ fn fail_with_last_error() {
|
|||||||
)"#);
|
)"#);
|
||||||
|
|
||||||
let result = call_vm!(vm, "", script, "", "");
|
let result = call_vm!(vm, "", script, "", "");
|
||||||
assert_eq!(result.ret_code, 1000);
|
|
||||||
assert_eq!(
|
let expected_error =
|
||||||
result.error_message,
|
CatchableError::LocalServiceError(1, Rc::new(r#""failed result from fallible_call_service""#.to_string()));
|
||||||
r#"Local service error, ret_code is 1, error message is '"failed result from fallible_call_service"'"#
|
assert!(check_error(&result, expected_error));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -53,11 +55,12 @@ fn fail_with_literals() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let result = call_vm!(vm, "", script, "", "");
|
let result = call_vm!(vm, "", script, "", "");
|
||||||
assert_eq!(result.ret_code, 1012);
|
|
||||||
assert_eq!(
|
let expected_error = CatchableError::FailWithoutXorError {
|
||||||
result.error_message,
|
ret_code: 1337,
|
||||||
"fail with ret_code '1337' and error_message 'error message' is used without corresponding xor"
|
error_message: "error message".to_string(),
|
||||||
);
|
};
|
||||||
|
assert!(check_error(&result, expected_error));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -78,7 +81,7 @@ fn fail_with_last_error_tetraplets() {
|
|||||||
)
|
)
|
||||||
"#);
|
"#);
|
||||||
|
|
||||||
let result = checked_call_vm!(vm, local_peer_id, script, "", "");
|
let _ = checked_call_vm!(vm, local_peer_id, script, "", "");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tetraplet_anchor.borrow()[0][0],
|
tetraplet_anchor.borrow()[0][0],
|
||||||
SecurityTetraplet::new(local_peer_id, fallible_service_id, local_fn_name, "")
|
SecurityTetraplet::new(local_peer_id, fallible_service_id, local_fn_name, "")
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use air::{PreparationError, ToErrorCode};
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -157,7 +158,8 @@ fn inner_fold_with_same_iterator() {
|
|||||||
|
|
||||||
let result = call_vm!(vm, "", script, "", "");
|
let result = call_vm!(vm, "", script, "", "");
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 1007);
|
let expected_error = PreparationError::AIRParseError("".to_string());
|
||||||
|
assert_eq!(result.ret_code, expected_error.to_error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use air::CatchableError;
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -163,8 +164,9 @@ fn match_with_equal_numbers() {
|
|||||||
(null)
|
(null)
|
||||||
)";
|
)";
|
||||||
|
|
||||||
let result = checked_call_vm!(vm, "asd", script, "", "");
|
let result = call_vm!(vm, "asd", script, "", "");
|
||||||
assert_eq!(result.ret_code, 0);
|
|
||||||
|
assert!(is_interpreter_succeded(&result));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -192,11 +194,13 @@ fn match_without_xor() {
|
|||||||
let result = call_vm!(set_variable_vm, "", &script, "", "");
|
let result = call_vm!(set_variable_vm, "", &script, "", "");
|
||||||
let result = call_vm!(vm, "", &script, "", result.data);
|
let result = call_vm!(vm, "", &script, "", result.data);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 1010);
|
let expected_error = CatchableError::MatchWithoutXorError;
|
||||||
|
assert!(check_error(&result, expected_error));
|
||||||
|
|
||||||
let result = call_vm!(vm, "", script, "", result.data);
|
let result = call_vm!(vm, "", script, "", result.data);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 1010);
|
let expected_error = CatchableError::MatchWithoutXorError;
|
||||||
|
assert!(check_error(&result, expected_error));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use air::CatchableError;
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -143,11 +144,13 @@ fn mismatch_without_xor() {
|
|||||||
let result = call_vm!(set_variable_vm, "asd", &script, "", "");
|
let result = call_vm!(set_variable_vm, "asd", &script, "", "");
|
||||||
let result = call_vm!(vm, "asd", &script, "", result.data);
|
let result = call_vm!(vm, "asd", &script, "", result.data);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 1011);
|
let expected_error = CatchableError::MismatchWithoutXorError;
|
||||||
|
assert!(check_error(&result, expected_error));
|
||||||
|
|
||||||
let result = call_vm!(vm, "asd", script, "", result.data);
|
let result = call_vm!(vm, "asd", script, "", result.data);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 1011);
|
let expected_error = CatchableError::MismatchWithoutXorError;
|
||||||
|
assert!(check_error(&result, expected_error));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -183,6 +186,5 @@ fn mismatch_with_two_xors() {
|
|||||||
let mut actual_trace = trace_from_result(&result);
|
let mut actual_trace = trace_from_result(&result);
|
||||||
let expected_executed_call_result = executed_state::request_sent_by(local_peer_id);
|
let expected_executed_call_result = executed_state::request_sent_by(local_peer_id);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 0);
|
|
||||||
assert_eq!(actual_trace.pop().unwrap(), expected_executed_call_result);
|
assert_eq!(actual_trace.pop().unwrap(), expected_executed_call_result);
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use air::UncatchableError;
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
|
use fstrings::f;
|
||||||
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn xor() {
|
fn xor() {
|
||||||
let local_peer_id = "local_peer_id";
|
let local_peer_id = "local_peer_id";
|
||||||
@ -89,32 +93,22 @@ fn xor_multiple_variables_found() {
|
|||||||
let mut set_variables_vm = create_avm(echo_call_service(), set_variables_peer_id);
|
let mut set_variables_vm = create_avm(echo_call_service(), set_variables_peer_id);
|
||||||
|
|
||||||
let local_peer_id = "local_peer_id";
|
let local_peer_id = "local_peer_id";
|
||||||
let mut vm = create_avm(echo_call_service(), local_peer_id);
|
let some_string = "some_string";
|
||||||
|
let expected_string = "expected_string";
|
||||||
let some_string = String::from("some_string");
|
let variable_name = "result_1";
|
||||||
let expected_string = String::from("expected_string");
|
let script = f!(r#"
|
||||||
let script = format!(
|
|
||||||
r#"
|
|
||||||
(seq
|
(seq
|
||||||
(call "{0}" ("service_id_1" "local_fn_name") ["{2}"] result_1)
|
(call "{set_variables_peer_id}" ("service_id_1" "local_fn_name") ["{some_string}"] {variable_name})
|
||||||
(xor
|
(xor
|
||||||
(call "{1}" ("service_id_1" "local_fn_name") [""] result_1)
|
(call "{local_peer_id}" ("service_id_1" "local_fn_name") [""] {variable_name})
|
||||||
(call "{1}" ("service_id_2" "local_fn_name") ["{3}"] result_2)
|
(call "{local_peer_id}" ("service_id_2" "local_fn_name") ["{expected_string}"] result_2)
|
||||||
)
|
)
|
||||||
)"#,
|
)"#);
|
||||||
set_variables_peer_id, local_peer_id, some_string, expected_string
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = checked_call_vm!(set_variables_vm, "asd", &script, "", "");
|
let result = call_vm!(set_variables_vm, "asd", &script, "", "");
|
||||||
let result = checked_call_vm!(vm, "asd", script, "", result.data);
|
|
||||||
|
|
||||||
let actual_trace = trace_from_result(&result);
|
let expected_error = UncatchableError::MultipleVariablesFound(variable_name.to_string());
|
||||||
let expected_trace = vec![
|
assert!(check_error(&result, expected_error));
|
||||||
executed_state::scalar_string(some_string),
|
|
||||||
executed_state::scalar_string(expected_string),
|
|
||||||
];
|
|
||||||
|
|
||||||
assert_eq!(actual_trace, expected_trace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use air::PreparationError;
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -160,5 +161,8 @@ fn invalid_air() {
|
|||||||
let script = r#"(seq )"#;
|
let script = r#"(seq )"#;
|
||||||
|
|
||||||
let result = call_vm!(vm, "", script, "", "");
|
let result = call_vm!(vm, "", script, "", "");
|
||||||
assert_eq!(result.ret_code, 1);
|
|
||||||
|
let error_message = air_parser::parse(script).expect_err("air parser should fail on this script");
|
||||||
|
let expected_error = PreparationError::AIRParseError(error_message);
|
||||||
|
assert!(check_error(&result, expected_error));
|
||||||
}
|
}
|
||||||
|
@ -74,9 +74,9 @@ fn flattening_scalar_arrays() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let result = checked_call_vm!(set_variable_vm, "asd", script.clone(), "", "");
|
let result = checked_call_vm!(set_variable_vm, "asd", script.clone(), "", "");
|
||||||
let result = checked_call_vm!(local_vm, "asd", script.clone(), "", result.data);
|
let result = call_vm!(local_vm, "asd", script.clone(), "", result.data);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 0);
|
assert!(is_interpreter_succeded(&result));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
closure_call_args.service_id_var,
|
closure_call_args.service_id_var,
|
||||||
Rc::new(RefCell::new("local_service_id".to_string()))
|
Rc::new(RefCell::new("local_service_id".to_string()))
|
||||||
@ -124,9 +124,9 @@ fn flattening_streams() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let result = checked_call_vm!(set_variable_vm, "asd", script.clone(), "", "");
|
let result = checked_call_vm!(set_variable_vm, "asd", script.clone(), "", "");
|
||||||
let result = checked_call_vm!(local_vm, "asd", script.clone(), "", result.data);
|
let result = call_vm!(local_vm, "asd", script.clone(), "", result.data);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 0);
|
assert!(is_interpreter_succeded(&result));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
closure_call_args.service_id_var,
|
closure_call_args.service_id_var,
|
||||||
Rc::new(RefCell::new("local_service_id".to_string()))
|
Rc::new(RefCell::new("local_service_id".to_string()))
|
||||||
@ -164,7 +164,7 @@ fn flattening_empty_values() {
|
|||||||
let result = checked_call_vm!(set_variable_vm, "asd", script.clone(), "", "");
|
let result = checked_call_vm!(set_variable_vm, "asd", script.clone(), "", "");
|
||||||
let result = checked_call_vm!(local_vm, "asd", script.clone(), "", result.data);
|
let result = checked_call_vm!(local_vm, "asd", script.clone(), "", result.data);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 0);
|
assert!(is_interpreter_succeded(&result));
|
||||||
assert_eq!(closure_call_args.args_var, Rc::new(RefCell::new(vec![])));
|
assert_eq!(closure_call_args.args_var, Rc::new(RefCell::new(vec![])));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use air::CatchableError;
|
||||||
|
use air::LambdaError;
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
use fstrings::f;
|
use fstrings::f;
|
||||||
@ -85,7 +87,6 @@ fn wait_on_stream_json_path_by_id() {
|
|||||||
let result = checked_call_vm!(local_vm, "", non_join_stream_script, "", "");
|
let result = checked_call_vm!(local_vm, "", non_join_stream_script, "", "");
|
||||||
let actual_trace = trace_from_result(&result);
|
let actual_trace = trace_from_result(&result);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 0);
|
|
||||||
assert_eq!(actual_trace.len(), 3);
|
assert_eq!(actual_trace.len(), 3);
|
||||||
|
|
||||||
let join_stream_script = format!(
|
let join_stream_script = format!(
|
||||||
@ -100,7 +101,6 @@ fn wait_on_stream_json_path_by_id() {
|
|||||||
let result = checked_call_vm!(local_vm, "", join_stream_script, "", "");
|
let result = checked_call_vm!(local_vm, "", join_stream_script, "", "");
|
||||||
let actual_trace = trace_from_result(&result);
|
let actual_trace = trace_from_result(&result);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 0);
|
|
||||||
assert_eq!(actual_trace.len(), 2); // par and the first call emit traces, second call doesn't
|
assert_eq!(actual_trace.len(), 2); // par and the first call emit traces, second call doesn't
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,17 +135,17 @@ fn wait_on_empty_stream_json_path() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dont_wait_on_json_path_on_scalars() {
|
fn dont_wait_on_json_path_on_scalars() {
|
||||||
let array = json!([1, 2, 3, 4, 5]);
|
let array = json!([1u32, 2u32, 3u32, 4u32, 5u32]);
|
||||||
|
|
||||||
let object = json!({
|
let object = json!({
|
||||||
"err_msg": "",
|
"err_msg": "",
|
||||||
"is_authenticated": 1,
|
"is_authenticated": 1i32,
|
||||||
"ret_code": 0,
|
"ret_code": 0i32,
|
||||||
});
|
});
|
||||||
|
|
||||||
let variables = maplit::hashmap!(
|
let variables = maplit::hashmap!(
|
||||||
"array".to_string() => array,
|
"array".to_string() => array.clone(),
|
||||||
"object".to_string() => object,
|
"object".to_string() => object.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let set_variables_call_service = set_variables_call_service(variables, VariableOptionSource::Argument(0));
|
let set_variables_call_service = set_variables_call_service(variables, VariableOptionSource::Argument(0));
|
||||||
@ -172,11 +172,10 @@ fn dont_wait_on_json_path_on_scalars() {
|
|||||||
let init_peer_id = "asd";
|
let init_peer_id = "asd";
|
||||||
let result = call_vm!(set_variable_vm, init_peer_id, &script, "", "");
|
let result = call_vm!(set_variable_vm, init_peer_id, &script, "", "");
|
||||||
let array_result = call_vm!(array_consumer, init_peer_id, &script, "", result.data.clone());
|
let array_result = call_vm!(array_consumer, init_peer_id, &script, "", result.data.clone());
|
||||||
assert_eq!(array_result.ret_code, 1003);
|
|
||||||
assert_eq!(
|
let expected_error =
|
||||||
array_result.error_message,
|
CatchableError::LambdaApplierError(LambdaError::ValueNotContainSuchArrayIdx { value: array, idx: 5 });
|
||||||
r#"value '[1,2,3,4,5]' does not contain element for idx = '5'"#
|
assert!(check_error(&array_result, expected_error));
|
||||||
);
|
|
||||||
|
|
||||||
let script = format!(
|
let script = format!(
|
||||||
r#"
|
r#"
|
||||||
@ -191,11 +190,13 @@ fn dont_wait_on_json_path_on_scalars() {
|
|||||||
let init_peer_id = "asd";
|
let init_peer_id = "asd";
|
||||||
let result = call_vm!(set_variable_vm, init_peer_id, &script, "", "");
|
let result = call_vm!(set_variable_vm, init_peer_id, &script, "", "");
|
||||||
let object_result = call_vm!(object_consumer, init_peer_id, script, "", result.data);
|
let object_result = call_vm!(object_consumer, init_peer_id, script, "", result.data);
|
||||||
assert_eq!(object_result.ret_code, 1003);
|
|
||||||
assert_eq!(
|
let expected_error = CatchableError::LambdaApplierError(LambdaError::ValueNotContainSuchField {
|
||||||
object_result.error_message,
|
value: object,
|
||||||
r#"value '{"err_msg":"","is_authenticated":1,"ret_code":0}' does not contain element with field name = 'non_exist_path'"#
|
field_name: "non_exist_path".to_string(),
|
||||||
);
|
});
|
||||||
|
|
||||||
|
assert!(check_error(&object_result, expected_error));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use air::CatchableError;
|
||||||
|
use air::LambdaError;
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
use fstrings::f;
|
use fstrings::f;
|
||||||
@ -27,20 +29,22 @@ fn lambda_not_allowed_for_non_objects_and_arrays() {
|
|||||||
let local_peer_id = "local_peer_id";
|
let local_peer_id = "local_peer_id";
|
||||||
let mut local_vm = create_avm(echo_call_service(), local_peer_id);
|
let mut local_vm = create_avm(echo_call_service(), local_peer_id);
|
||||||
|
|
||||||
let script = format!(
|
let some_string = "some_string";
|
||||||
r#"
|
let script = f!(r#"
|
||||||
(seq
|
(seq
|
||||||
(call "{0}" ("" "") ["some_string"] string_variable)
|
(call "{set_variable_peer_id}" ("" "") ["{some_string}"] string_variable)
|
||||||
(call "{1}" ("" "") [string_variable.$.some_lambda])
|
(call "{local_peer_id}" ("" "") [string_variable.$.some_lambda])
|
||||||
)
|
)
|
||||||
"#,
|
"#);
|
||||||
set_variable_peer_id, local_peer_id
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = checked_call_vm!(set_variable_vm, "asd", &script, "", "");
|
let result = checked_call_vm!(set_variable_vm, "asd", &script, "", "");
|
||||||
let result = call_vm!(local_vm, "asd", script, "", result.data);
|
let result = call_vm!(local_vm, "asd", script, "", result.data);
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 1003);
|
let expected_error = CatchableError::LambdaApplierError(LambdaError::FieldAccessorNotMatchValue {
|
||||||
|
value: json!(some_string),
|
||||||
|
field_name: "some_lambda".to_string(),
|
||||||
|
});
|
||||||
|
assert!(check_error(&result, expected_error));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -14,8 +14,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use air::UncatchableError;
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
use pretty_assertions::assert_eq;
|
use air_trace_handler::TraceHandlerError;
|
||||||
|
use air_trace_handler::{CallResultError, MergeError};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn par_early_exit() {
|
fn par_early_exit() {
|
||||||
@ -141,7 +143,20 @@ fn par_early_exit() {
|
|||||||
];
|
];
|
||||||
let setter_3_malicious_data = raw_data_from_trace(setter_3_malicious_trace);
|
let setter_3_malicious_data = raw_data_from_trace(setter_3_malicious_trace);
|
||||||
let init_result_3 = call_vm!(init, "", &script, init_result_2.data.clone(), setter_3_malicious_data);
|
let init_result_3 = call_vm!(init, "", &script, init_result_2.data.clone(), setter_3_malicious_data);
|
||||||
assert_eq!(init_result_3.ret_code, 1013);
|
|
||||||
|
let expected_error = UncatchableError::TraceError(TraceHandlerError::MergeError(MergeError::IncorrectCallResult(
|
||||||
|
CallResultError::ValuesNotEqual {
|
||||||
|
prev_value: Value::Stream {
|
||||||
|
value: rc!(json!("1")),
|
||||||
|
generation: 0,
|
||||||
|
},
|
||||||
|
current_value: Value::Stream {
|
||||||
|
value: rc!(json!("non_exist_value")),
|
||||||
|
generation: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
assert!(check_error(&init_result_3, expected_error));
|
||||||
|
|
||||||
let actual_trace = trace_from_result(&init_result_3);
|
let actual_trace = trace_from_result(&init_result_3);
|
||||||
let expected_trace = trace_from_result(&init_result_2);
|
let expected_trace = trace_from_result(&init_result_2);
|
||||||
|
@ -62,5 +62,6 @@ fn issue_137() {
|
|||||||
let node_2_result = checked_call_vm!(node_2, "", &script, "", initiator_result.data);
|
let node_2_result = checked_call_vm!(node_2, "", &script, "", initiator_result.data);
|
||||||
let node_4_result_1 = checked_call_vm!(node_4, "", &script, "", node_1_result.data);
|
let node_4_result_1 = checked_call_vm!(node_4, "", &script, "", node_1_result.data);
|
||||||
let result = call_vm!(node_4, "", script, node_4_result_1.data, node_2_result.data);
|
let result = call_vm!(node_4, "", script, node_4_result_1.data, node_2_result.data);
|
||||||
assert_eq!(result.ret_code, 0);
|
|
||||||
|
assert!(is_interpreter_succeded(&result));
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ impl<'i> Variable<'i> {
|
|||||||
Self::Stream(Stream::new(name, position))
|
Self::Stream(Stream::new(name, position))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &'i str {
|
||||||
match self {
|
match self {
|
||||||
Variable::Scalar(scalar) => scalar.name,
|
Variable::Scalar(scalar) => scalar.name,
|
||||||
Variable::Stream(stream) => stream.name,
|
Variable::Stream(stream) => stream.name,
|
||||||
@ -113,7 +113,7 @@ impl<'i> VariableWithLambda<'i> {
|
|||||||
Self::Stream(StreamWithLambda::new(name, Some(lambda), position))
|
Self::Stream(StreamWithLambda::new(name, Some(lambda), position))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &'i str {
|
||||||
match self {
|
match self {
|
||||||
VariableWithLambda::Scalar(scalar) => scalar.name,
|
VariableWithLambda::Scalar(scalar) => scalar.name,
|
||||||
VariableWithLambda::Stream(stream) => stream.name,
|
VariableWithLambda::Stream(stream) => stream.name,
|
||||||
|
@ -142,6 +142,9 @@ fn parser_error_to_label(file_id: usize, error: ParserError) -> Label<usize> {
|
|||||||
IteratorRestrictionNotAllowed(start, end, _) => {
|
IteratorRestrictionNotAllowed(start, end, _) => {
|
||||||
Label::primary(file_id, start..end).with_message(error.to_string())
|
Label::primary(file_id, start..end).with_message(error.to_string())
|
||||||
}
|
}
|
||||||
|
MultipleIterableValues(start, end, _) => {
|
||||||
|
Label::primary(file_id, start..end).with_message(error.to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,9 @@ pub enum ParserError {
|
|||||||
|
|
||||||
#[error("new can't be applied to a '{2}' because it's an iterator")]
|
#[error("new can't be applied to a '{2}' because it's an iterator")]
|
||||||
IteratorRestrictionNotAllowed(usize, usize, String),
|
IteratorRestrictionNotAllowed(usize, usize, String),
|
||||||
|
|
||||||
|
#[error("multiple iterable values found for iterable name '{2}'")]
|
||||||
|
MultipleIterableValues(usize, usize, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::convert::Infallible> for ParserError {
|
impl From<std::convert::Infallible> for ParserError {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
pub left: usize,
|
pub left: usize,
|
||||||
pub right: usize,
|
pub right: usize,
|
||||||
@ -28,7 +28,35 @@ impl Span {
|
|||||||
Self { left, right }
|
Self { left, right }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, position: usize) -> bool {
|
pub fn contains_position(&self, position: usize) -> bool {
|
||||||
self.left < position && position < self.right
|
self.left < position && position < self.right
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains_span(&self, span: Self) -> bool {
|
||||||
|
self.contains_position(span.left) && self.contains_position(span.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
impl PartialOrd for Span {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
let self_min = std::cmp::min(self.left, self.right);
|
||||||
|
let other_min = std::cmp::min(other.left, other.right);
|
||||||
|
|
||||||
|
if self_min < other_min {
|
||||||
|
Some(Ordering::Less)
|
||||||
|
} else if self == other {
|
||||||
|
Some(Ordering::Equal)
|
||||||
|
} else {
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Span {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
// it's safe since partial_cmp always returns Some
|
||||||
|
self.partial_cmp(other).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,89 @@ fn parse_undefined_iterable() {
|
|||||||
assert!(matches!(parser_error, ParserError::UndefinedIterable(..)));
|
assert!(matches!(parser_error, ParserError::UndefinedIterable(..)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_fold_with_undefined_iterable() {
|
||||||
|
let source_code = r#"
|
||||||
|
(seq
|
||||||
|
(null)
|
||||||
|
(fold iterable i
|
||||||
|
(seq
|
||||||
|
(call "" ("" "") ["hello" ""] $void)
|
||||||
|
(next i)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let lexer = crate::AIRLexer::new(source_code);
|
||||||
|
|
||||||
|
let parser = crate::AIRParser::new();
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let mut validator = crate::parser::VariableValidator::new();
|
||||||
|
parser
|
||||||
|
.parse(source_code, &mut errors, &mut validator, lexer)
|
||||||
|
.expect("parser shouldn't fail");
|
||||||
|
|
||||||
|
let errors = validator.finalize();
|
||||||
|
|
||||||
|
assert_eq!(errors.len(), 1);
|
||||||
|
|
||||||
|
let error = &errors[0].error;
|
||||||
|
let parser_error = match error {
|
||||||
|
ParseError::User { error } => error,
|
||||||
|
_ => panic!("unexpected error type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(matches!(parser_error, ParserError::UndefinedVariable(..)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_fold_with_multiple_iterator() {
|
||||||
|
let source_code = r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call "" ("" "") [] iterable_1)
|
||||||
|
(call "" ("" "") [] iterable_2)
|
||||||
|
)
|
||||||
|
(fold iterable_1 i
|
||||||
|
(seq
|
||||||
|
(fold iterable_2 i
|
||||||
|
(seq
|
||||||
|
(call "" ("" "") ["hello" ""] $void)
|
||||||
|
(next i)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(next i)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let lexer = crate::AIRLexer::new(source_code);
|
||||||
|
|
||||||
|
let parser = crate::AIRParser::new();
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let mut validator = crate::parser::VariableValidator::new();
|
||||||
|
parser
|
||||||
|
.parse(source_code, &mut errors, &mut validator, lexer)
|
||||||
|
.expect("parser shouldn't fail");
|
||||||
|
|
||||||
|
let errors = validator.finalize();
|
||||||
|
|
||||||
|
assert_eq!(errors.len(), 1);
|
||||||
|
|
||||||
|
let error = &errors[0].error;
|
||||||
|
let parser_error = match error {
|
||||||
|
ParseError::User { error } => error,
|
||||||
|
_ => panic!("unexpected error type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
parser_error,
|
||||||
|
ParserError::MultipleIterableValues(..)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_fold() {
|
fn parse_fold() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
|
@ -25,6 +25,7 @@ use lalrpop_util::ParseError;
|
|||||||
|
|
||||||
use multimap::MultiMap;
|
use multimap::MultiMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
/// Intermediate implementation of variable validator.
|
/// Intermediate implementation of variable validator.
|
||||||
///
|
///
|
||||||
@ -36,10 +37,10 @@ use std::collections::HashMap;
|
|||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct VariableValidator<'i> {
|
pub struct VariableValidator<'i> {
|
||||||
/// Contains the most left definition of a variables met in call outputs.
|
/// Contains the most left definition of a variables met in call outputs.
|
||||||
met_variables: HashMap<&'i str, Span>,
|
met_variable_definitions: HashMap<&'i str, Span>,
|
||||||
|
|
||||||
/// Contains iterables met in fold iterables.
|
/// Contains iterables met in fold iterables.
|
||||||
met_iterators: MultiMap<&'i str, Span>,
|
met_iterator_definitions: MultiMap<&'i str, Span>,
|
||||||
|
|
||||||
/// These variables from calls and folds haven't been resolved at the first meet.
|
/// These variables from calls and folds haven't been resolved at the first meet.
|
||||||
unresolved_variables: MultiMap<&'i str, Span>,
|
unresolved_variables: MultiMap<&'i str, Span>,
|
||||||
@ -92,7 +93,7 @@ impl<'i> VariableValidator<'i> {
|
|||||||
|
|
||||||
pub(super) fn met_new(&mut self, new: &New<'i>, span: Span) {
|
pub(super) fn met_new(&mut self, new: &New<'i>, span: Span) {
|
||||||
self.check_for_non_iterators
|
self.check_for_non_iterators
|
||||||
.push((variable_name(&new.variable), span));
|
.push((new.variable.name(), span));
|
||||||
// new defines a new variable
|
// new defines a new variable
|
||||||
self.met_variable_definition(&new.variable, span);
|
self.met_variable_definition(&new.variable, span);
|
||||||
}
|
}
|
||||||
@ -120,7 +121,7 @@ impl<'i> VariableValidator<'i> {
|
|||||||
self.met_variable_definition(&ap.result, span);
|
self.met_variable_definition(&ap.result, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn finalize(&self) -> Vec<ErrorRecovery<usize, Token<'i>, ParserError>> {
|
pub(super) fn finalize(self) -> Vec<ErrorRecovery<usize, Token<'i>, ParserError>> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
for (name, span) in self.unresolved_variables.iter() {
|
for (name, span) in self.unresolved_variables.iter() {
|
||||||
if !self.contains_variable(name, *span) {
|
if !self.contains_variable(name, *span) {
|
||||||
@ -140,6 +141,19 @@ impl<'i> VariableValidator<'i> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (name, mut spans) in self.met_iterator_definitions.into_iter() {
|
||||||
|
spans.sort();
|
||||||
|
let mut prev_span: Option<Span> = None;
|
||||||
|
for span in spans {
|
||||||
|
match prev_span {
|
||||||
|
Some(prev_span) if prev_span.contains_span(span) => {
|
||||||
|
add_to_errors(name, &mut errors, span, Token::Fold)
|
||||||
|
}
|
||||||
|
Some(_) | None => prev_span = Some(span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,8 +183,7 @@ impl<'i> VariableValidator<'i> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn met_variable_wl(&mut self, variable: &VariableWithLambda<'i>, span: Span) {
|
fn met_variable_wl(&mut self, variable: &VariableWithLambda<'i>, span: Span) {
|
||||||
let name = variable_wl_name(variable);
|
self.met_variable_name(variable.name(), span);
|
||||||
self.met_variable_name(name, span);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn met_variable_name(&mut self, name: &'i str, span: Span) {
|
fn met_variable_name(&mut self, name: &'i str, span: Span) {
|
||||||
@ -180,13 +193,13 @@ impl<'i> VariableValidator<'i> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn contains_variable(&self, key: &str, key_span: Span) -> bool {
|
fn contains_variable(&self, key: &str, key_span: Span) -> bool {
|
||||||
if let Some(found_span) = self.met_variables.get(key) {
|
if let Some(found_span) = self.met_variable_definitions.get(key) {
|
||||||
if found_span < &key_span {
|
if found_span < &key_span {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let found_spans = match self.met_iterators.get_vec(key) {
|
let found_spans = match self.met_iterator_definitions.get_vec(key) {
|
||||||
Some(found_spans) => found_spans,
|
Some(found_spans) => found_spans,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
@ -195,14 +208,13 @@ impl<'i> VariableValidator<'i> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn met_variable_definition(&mut self, variable: &Variable<'i>, span: Span) {
|
fn met_variable_definition(&mut self, variable: &Variable<'i>, span: Span) {
|
||||||
let name = variable_name(variable);
|
self.met_variable_name_definition(variable.name(), span);
|
||||||
self.met_variable_name_definition(name, span);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn met_variable_name_definition(&mut self, name: &'i str, span: Span) {
|
fn met_variable_name_definition(&mut self, name: &'i str, span: Span) {
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
match self.met_variables.entry(name) {
|
match self.met_variable_definitions.entry(name) {
|
||||||
Entry::Occupied(occupied) => {
|
Entry::Occupied(occupied) => {
|
||||||
if occupied.get() > &span {
|
if occupied.get() > &span {
|
||||||
*occupied.into_mut() = span;
|
*occupied.into_mut() = span;
|
||||||
@ -228,7 +240,7 @@ impl<'i> VariableValidator<'i> {
|
|||||||
|
|
||||||
/// Checks that multimap contains a span for given key such that provided span lies inside it.
|
/// Checks that multimap contains a span for given key such that provided span lies inside it.
|
||||||
fn contains_iterable(&self, key: &str, key_span: Span) -> bool {
|
fn contains_iterable(&self, key: &str, key_span: Span) -> bool {
|
||||||
let found_spans = match self.met_iterators.get_vec(key) {
|
let found_spans = match self.met_iterator_definitions.get_vec(key) {
|
||||||
Some(found_spans) => found_spans,
|
Some(found_spans) => found_spans,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
@ -239,25 +251,7 @@ impl<'i> VariableValidator<'i> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn met_iterator_definition(&mut self, iterator: &Scalar<'i>, span: Span) {
|
fn met_iterator_definition(&mut self, iterator: &Scalar<'i>, span: Span) {
|
||||||
self.met_iterators.insert(iterator.name, span);
|
self.met_iterator_definitions.insert(iterator.name, span);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
impl PartialOrd for Span {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
let self_min = std::cmp::min(self.left, self.right);
|
|
||||||
let other_min = std::cmp::min(other.left, other.right);
|
|
||||||
|
|
||||||
if self_min < other_min {
|
|
||||||
Some(Ordering::Less)
|
|
||||||
} else if self == other {
|
|
||||||
Some(Ordering::Equal)
|
|
||||||
} else {
|
|
||||||
Some(Ordering::Greater)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,6 +267,7 @@ fn add_to_errors<'err, 'i>(
|
|||||||
Token::New => {
|
Token::New => {
|
||||||
ParserError::IteratorRestrictionNotAllowed(span.left, span.right, variable_name)
|
ParserError::IteratorRestrictionNotAllowed(span.left, span.right, variable_name)
|
||||||
}
|
}
|
||||||
|
Token::Fold => ParserError::MultipleIterableValues(span.left, span.right, variable_name),
|
||||||
_ => ParserError::UndefinedVariable(span.left, span.right, variable_name),
|
_ => ParserError::UndefinedVariable(span.left, span.right, variable_name),
|
||||||
};
|
};
|
||||||
let error = ParseError::User { error };
|
let error = ParseError::User { error };
|
||||||
@ -286,17 +281,3 @@ fn add_to_errors<'err, 'i>(
|
|||||||
|
|
||||||
errors.push(error);
|
errors.push(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variable_name<'v>(variable: &Variable<'v>) -> &'v str {
|
|
||||||
match variable {
|
|
||||||
Variable::Scalar(scalar) => scalar.name,
|
|
||||||
Variable::Stream(stream) => stream.name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variable_wl_name<'v>(variable: &VariableWithLambda<'v>) -> &'v str {
|
|
||||||
match variable {
|
|
||||||
VariableWithLambda::Scalar(scalar) => scalar.name,
|
|
||||||
VariableWithLambda::Stream(stream) => stream.name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -106,3 +106,28 @@ pub fn print_trace(result: &RawAVMOutcome, trace_name: &str) {
|
|||||||
}
|
}
|
||||||
println!("]");
|
println!("]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! rc {
|
||||||
|
($expr:expr) => {
|
||||||
|
std::rc::Rc::new($expr)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
use air::ToErrorCode;
|
||||||
|
use air_interpreter_interface::INTERPRETER_SUCCESS;
|
||||||
|
|
||||||
|
pub fn is_interpreter_succeded(result: &RawAVMOutcome) -> bool {
|
||||||
|
result.ret_code == INTERPRETER_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_error(result: &RawAVMOutcome, error: impl ToErrorCode + ToString) -> bool {
|
||||||
|
println!(
|
||||||
|
"{} == {} || {} == {}",
|
||||||
|
result.ret_code,
|
||||||
|
error.to_error_code(),
|
||||||
|
result.error_message,
|
||||||
|
error.to_string()
|
||||||
|
);
|
||||||
|
result.ret_code == error.to_error_code() && result.error_message == error.to_string()
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user