mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 15:20:16 +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>,
|
||||
ap_result: &MergerApResult,
|
||||
) -> ExecutionResult<()> {
|
||||
use crate::execution_step::ExecutionError::ApResultNotCorrespondToInstr;
|
||||
use crate::execution_step::UncatchableError::ApResultNotCorrespondToInstr;
|
||||
use ast::Variable::*;
|
||||
|
||||
match (variable, generation) {
|
||||
(Stream(_), Some(_)) => 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);
|
||||
exec_ctx.tracker.meet_call();
|
||||
|
||||
let resolved_call = joinable!(ResolvedCall::new(self, exec_ctx), exec_ctx).map_err(|e| {
|
||||
set_last_error(self, exec_ctx, e.clone(), None);
|
||||
e
|
||||
})?;
|
||||
let resolved_call = joinable!(ResolvedCall::new(self, exec_ctx), exec_ctx)
|
||||
.map_err(|e| set_last_error(self, exec_ctx, e, None))?;
|
||||
|
||||
let tetraplet = resolved_call.as_tetraplet();
|
||||
joinable!(resolved_call.execute(exec_ctx, trace_ctx), exec_ctx).map_err(|e| {
|
||||
set_last_error(self, exec_ctx, e.clone(), Some(tetraplet));
|
||||
e
|
||||
})
|
||||
joinable!(resolved_call.execute(exec_ctx, trace_ctx), exec_ctx)
|
||||
.map_err(|e| set_last_error(self, exec_ctx, e, Some(tetraplet)))
|
||||
}
|
||||
}
|
||||
|
||||
fn set_last_error<'i>(
|
||||
call: &Call<'i>,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
e: Rc<ExecutionError>,
|
||||
execution_error: ExecutionError,
|
||||
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 {
|
||||
// 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
|
||||
@ -67,10 +68,16 @@ fn set_last_error<'i>(
|
||||
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 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_could_be_set = false;
|
||||
|
||||
ExecutionError::Catchable(catchable_error)
|
||||
}
|
||||
|
@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::exec_err;
|
||||
use crate::execution_step::air::call::call_result_setter::set_result_from_value;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
|
||||
use air_interpreter_data::CallResult;
|
||||
@ -51,7 +51,7 @@ pub(super) fn handle_prev_state<'i>(
|
||||
let ret_code = *ret_code;
|
||||
let err_msg = err_msg.clone();
|
||||
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 })
|
||||
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 = ExecutionError::LocalServiceError(service_result.ret_code, error_message.clone());
|
||||
let error = Rc::new(error);
|
||||
let error = CatchableError::LocalServiceError(service_result.ret_code, error_message.clone());
|
||||
|
||||
trace_ctx.meet_call_end(CallServiceFailed(service_result.ret_code, error_message));
|
||||
|
||||
Err(error)
|
||||
Err(error.into())
|
||||
}
|
||||
|
||||
fn try_to_service_result(
|
||||
@ -152,7 +151,7 @@ fn try_to_service_result(
|
||||
let error = CallServiceFailed(i32::MAX, error_msg.clone());
|
||||
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 crate::execution_step::RSecurityTetraplet;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
use crate::execution_step::UncatchableError;
|
||||
use crate::trace_to_exec_err;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
@ -187,10 +188,10 @@ fn check_output_name(output: &ast::CallOutputValue<'_>, exec_ctx: &ExecutionCtx<
|
||||
if exec_ctx.scalars.shadowing_allowed() {
|
||||
Ok(())
|
||||
} 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(()),
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,8 @@
|
||||
*/
|
||||
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use crate::exec_err;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::JValue;
|
||||
|
||||
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> {
|
||||
match jvalue {
|
||||
JValue::String(s) => Ok(s),
|
||||
_ => exec_err!(ExecutionError::IncompatibleJValueType {
|
||||
_ => Err(CatchableError::IncompatibleJValueType {
|
||||
variable_name: variable.name().to_string(),
|
||||
actual_value: jvalue,
|
||||
expected_value_type: "string",
|
||||
}),
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,10 @@
|
||||
*/
|
||||
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::LastErrorDescriptor;
|
||||
use super::TraceHandler;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::log_instruction;
|
||||
|
||||
use air_parser::ast::Fail;
|
||||
@ -48,7 +48,7 @@ fn fail_with_literals<'i>(
|
||||
fail: &Fail<'_>,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<()> {
|
||||
let fail_error = ExecutionError::FailWithoutXorError {
|
||||
let fail_error = CatchableError::FailWithoutXorError {
|
||||
ret_code,
|
||||
error_message: error_message.to_string(),
|
||||
};
|
||||
@ -71,7 +71,7 @@ fn fail_with_literals<'i>(
|
||||
|
||||
update_context_state(exec_ctx);
|
||||
|
||||
Err(fail_error)
|
||||
Err(fail_error.into())
|
||||
}
|
||||
|
||||
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);
|
||||
Err(last_error)
|
||||
Err(last_error.into())
|
||||
}
|
||||
|
||||
fn update_context_state(exec_ctx: &mut ExecutionCtx<'_>) {
|
||||
|
@ -22,7 +22,6 @@ pub(crate) use fold_state::IterableType;
|
||||
pub(super) use utils::*;
|
||||
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::Instruction;
|
||||
use super::ScalarRef;
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::exec_err;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::JValue;
|
||||
use crate::LambdaAST;
|
||||
@ -107,11 +107,12 @@ fn from_call_result(call_result: ValueAggregate, variable_name: &str) -> Executi
|
||||
array.len()
|
||||
}
|
||||
v => {
|
||||
return exec_err!(ExecutionError::IncompatibleJValueType {
|
||||
return Err(CatchableError::IncompatibleJValueType {
|
||||
variable_name: variable_name.to_string(),
|
||||
actual_value: (*v).clone(),
|
||||
expected_value_type: "array",
|
||||
})
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
@ -156,10 +157,7 @@ fn from_jvalue(
|
||||
let iterable = match jvalue {
|
||||
JValue::Array(array) => array,
|
||||
_ => {
|
||||
return exec_err!(ExecutionError::FoldIteratesOverNonArray(
|
||||
jvalue.clone(),
|
||||
formatted_lambda_ast
|
||||
))
|
||||
return Err(CatchableError::FoldIteratesOverNonArray(jvalue.clone(), formatted_lambda_ast).into());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
use super::compare_matchable::are_matchable_eq;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::TraceHandler;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::execution_step::Joinable;
|
||||
use crate::joinable;
|
||||
use crate::log_instruction;
|
||||
@ -35,7 +35,7 @@ impl<'i> super::ExecutableInstruction<'i> for Match<'i> {
|
||||
)?;
|
||||
|
||||
if !are_values_equal {
|
||||
return crate::exec_err!(ExecutionError::MatchWithoutXorError);
|
||||
return Err(CatchableError::MatchWithoutXorError.into());
|
||||
}
|
||||
|
||||
self.instruction.execute(exec_ctx, trace_ctx)
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
use super::compare_matchable::are_matchable_eq;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::TraceHandler;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::execution_step::Joinable;
|
||||
use crate::joinable;
|
||||
use crate::log_instruction;
|
||||
@ -35,7 +35,7 @@ impl<'i> super::ExecutableInstruction<'i> for MisMatch<'i> {
|
||||
)?;
|
||||
|
||||
if are_values_equal {
|
||||
return crate::exec_err!(ExecutionError::MismatchWithoutXorError);
|
||||
return Err(CatchableError::MismatchWithoutXorError.into());
|
||||
}
|
||||
|
||||
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::ValueAggregate;
|
||||
use super::execution_context::*;
|
||||
use super::Catchable;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
@ -47,48 +46,68 @@ use air_parser::ast::Instruction;
|
||||
|
||||
/// Executes instruction and updates last error if needed.
|
||||
macro_rules! execute {
|
||||
($self:expr, $instr:expr, $exec_ctx:ident, $trace_ctx:ident) => {
|
||||
match $instr.execute($exec_ctx, $trace_ctx) {
|
||||
Err(e) => {
|
||||
if !$exec_ctx.last_error_could_be_set {
|
||||
return Err(e);
|
||||
}
|
||||
($self:expr, $instr:expr, $exec_ctx:ident, $trace_ctx:ident) => {{
|
||||
let result = $instr.execute($exec_ctx, $trace_ctx);
|
||||
|
||||
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,
|
||||
if !$exec_ctx.last_error_could_be_set {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
/// MatchWithoutXorError or MismatchWithoutXorError.
|
||||
macro_rules! execute_match_mismatch {
|
||||
($self:expr, $instr:expr, $exec_ctx:ident, $trace_ctx:ident) => {
|
||||
match $instr.execute($exec_ctx, $trace_ctx) {
|
||||
Err(e) => {
|
||||
use std::borrow::Borrow;
|
||||
macro_rules! execute_match_or_mismatch {
|
||||
($self:expr, $instr:expr, $exec_ctx:ident, $trace_ctx:ident) => {{
|
||||
let result = $instr.execute($exec_ctx, $trace_ctx);
|
||||
let execution_error = match result {
|
||||
Err(e) => e,
|
||||
v => return v,
|
||||
};
|
||||
|
||||
if !$exec_ctx.last_error_could_be_set
|
||||
|| matches!(&*e.borrow(), ExecutionError::MatchWithoutXorError)
|
||||
|| 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,
|
||||
if !$exec_ctx.last_error_could_be_set || execution_error.is_match_or_mismatch() {
|
||||
return Err(execution_error);
|
||||
}
|
||||
};
|
||||
|
||||
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> {
|
||||
@ -114,8 +133,8 @@ impl<'i> ExecutableInstruction<'i> for Instruction<'i> {
|
||||
Instruction::Xor(xor) => execute!(self, xor, exec_ctx, trace_ctx),
|
||||
|
||||
// match/mismatch shouldn't rewrite last_error
|
||||
Instruction::Match(match_) => execute_match_mismatch!(self, match_, exec_ctx, trace_ctx),
|
||||
Instruction::MisMatch(mismatch) => execute_match_mismatch!(self, mismatch, exec_ctx, trace_ctx),
|
||||
Instruction::Match(match_) => execute_match_or_mismatch!(self, match_, 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."),
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
mod completeness_updater;
|
||||
|
||||
use super::Catchable;
|
||||
use super::ExecutableInstruction;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
@ -30,8 +29,6 @@ use completeness_updater::ParCompletenessUpdater;
|
||||
use air_parser::ast::Par;
|
||||
use air_trace_handler::SubtreeType;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl<'i> ExecutableInstruction<'i> for Par<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
@ -84,7 +81,7 @@ fn execute_subtree<'i>(
|
||||
|
||||
enum SubtreeResult {
|
||||
Succeeded,
|
||||
Failed(Rc<ExecutionError>),
|
||||
Failed(ExecutionError),
|
||||
}
|
||||
|
||||
fn prepare_par_result(
|
||||
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::Catchable;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
@ -42,12 +41,13 @@ impl<'i> super::ExecutableInstruction<'i> for Xor<'i> {
|
||||
}
|
||||
|
||||
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
|
||||
// to a corresponding xor. They'll become errors iff there is no such xor and execution_step is
|
||||
// bubble up until the very beginning of current subtree. So the error message shouldn't
|
||||
// be print out in order not to confuse users.
|
||||
ExecutionError::MatchWithoutXorError | ExecutionError::MismatchWithoutXorError => {}
|
||||
e => log::warn!("xor caught an error: {}", e),
|
||||
return;
|
||||
}
|
||||
|
||||
log::warn!("xor caught an error: {}", e);
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ mod resolved_call_result;
|
||||
mod stream;
|
||||
|
||||
use super::iterable::IterableItem;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::ValueAggregate;
|
||||
use crate::execution_step::lambda_applier::*;
|
||||
|
@ -14,12 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::ExecutionError::LambdaApplierError;
|
||||
use super::ExecutionResult;
|
||||
use super::JValuable;
|
||||
use super::LambdaAST;
|
||||
use super::LambdaError::EmptyStream;
|
||||
use crate::exec_err;
|
||||
use crate::execution_step::CatchableError::LambdaApplierError;
|
||||
use crate::execution_step::ExecutionCtx;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
@ -30,7 +29,7 @@ use std::borrow::Cow;
|
||||
impl JValuable for () {
|
||||
fn apply_lambda<'i>(&self, _lambda: &LambdaAST<'_>, _exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||
// 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>(
|
||||
@ -39,7 +38,7 @@ impl JValuable for () {
|
||||
_exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
||||
// 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> {
|
||||
|
@ -17,7 +17,6 @@
|
||||
use super::select_from_stream;
|
||||
use super::ExecutionResult;
|
||||
use super::JValuable;
|
||||
use crate::exec_err;
|
||||
use crate::execution_step::boxed_value::Generation;
|
||||
use crate::execution_step::boxed_value::Stream;
|
||||
use crate::execution_step::ExecutionCtx;
|
||||
@ -89,7 +88,7 @@ impl<'stream> StreamJvaluableIngredients<'stream> {
|
||||
}
|
||||
|
||||
pub(self) fn iter(&self) -> ExecutionResult<StreamIter<'_>> {
|
||||
use super::ExecutionError::StreamDontHaveSuchGeneration;
|
||||
use crate::execution_step::CatchableError::StreamDontHaveSuchGeneration;
|
||||
|
||||
match self.stream.iter(self.generation) {
|
||||
Some(iter) => Ok(iter),
|
||||
@ -99,10 +98,7 @@ impl<'stream> StreamJvaluableIngredients<'stream> {
|
||||
Generation::Last => unreachable!(),
|
||||
};
|
||||
|
||||
exec_err!(StreamDontHaveSuchGeneration(
|
||||
self.stream.deref().clone(),
|
||||
generation as usize
|
||||
))
|
||||
Err(StreamDontHaveSuchGeneration(self.stream.deref().clone(), generation as usize).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ mod scalar;
|
||||
mod stream;
|
||||
mod variable;
|
||||
|
||||
pub(crate) use super::ExecutionError;
|
||||
pub(crate) use iterable::*;
|
||||
pub(crate) use jvaluable::*;
|
||||
pub(crate) use scalar::ScalarRef;
|
||||
|
@ -14,10 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::ValueAggregate;
|
||||
use crate::exec_err;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::JValue;
|
||||
|
||||
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
|
||||
/// a peer is equal to number of the interpreter runs in context of one particle. And each set of
|
||||
/// obtained values from a current_data that were not present in prev_data becomes a new generation.
|
||||
// TODO: make it non-pub after boxed value refactoring.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(crate) struct Stream(Vec<Vec<ValueAggregate>>);
|
||||
pub struct Stream(Vec<Vec<ValueAggregate>>);
|
||||
|
||||
impl Stream {
|
||||
pub(crate) fn from_generations_count(count: usize) -> Self {
|
||||
@ -51,7 +49,7 @@ impl Stream {
|
||||
};
|
||||
|
||||
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);
|
||||
|
@ -14,19 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod catchable;
|
||||
mod joinable;
|
||||
|
||||
pub(crate) use catchable::Catchable;
|
||||
pub(crate) use joinable::Joinable;
|
||||
pub(crate) use super::Joinable;
|
||||
|
||||
use super::Stream;
|
||||
use crate::execution_step::lambda_applier::LambdaError;
|
||||
use crate::JValue;
|
||||
use crate::ToErrorCode;
|
||||
|
||||
use air_trace_handler::MergerApResult;
|
||||
use air_trace_handler::TraceHandlerError;
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::EnumDiscriminants;
|
||||
use strum_macros::EnumIter;
|
||||
@ -34,26 +28,22 @@ use thiserror::Error as ThisError;
|
||||
|
||||
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)]
|
||||
#[strum_discriminants(derive(EnumIter))]
|
||||
pub(crate) enum ExecutionError {
|
||||
pub enum CatchableError {
|
||||
/// An error is occurred while calling local service via call_service.
|
||||
#[error("Local service error, ret_code is {0}, error message is '{1}'")]
|
||||
LocalServiceError(i32, Rc<String>),
|
||||
|
||||
/// 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")]
|
||||
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.
|
||||
#[error(
|
||||
"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,
|
||||
},
|
||||
|
||||
/// 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.
|
||||
#[error("lambda '{1}' returned non-array value '{0}' for fold instruction")]
|
||||
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.
|
||||
#[error("match is used without corresponding xor")]
|
||||
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")]
|
||||
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)]
|
||||
TraceError(#[from] TraceHandlerError),
|
||||
LambdaApplierError(#[from] LambdaError),
|
||||
|
||||
/// Errors occurred while insertion of a value inside stream that doesn't have corresponding generation.
|
||||
#[error("stream {0:?} doesn't have generation with number {1}, probably a supplied to the interpreter data is corrupted")]
|
||||
StreamDontHaveSuchGeneration(Stream, usize),
|
||||
|
||||
/// Errors occurred when result from data doesn't match to a instruction, f.e. an instruction
|
||||
/// could be applied to a stream, but result doesn't contain generation in a source position.
|
||||
#[error("ap result {0:?} doesn't match corresponding instruction")]
|
||||
ApResultNotCorrespondToInstr(MergerApResult),
|
||||
}
|
||||
|
||||
impl From<LambdaError> for Rc<ExecutionError> {
|
||||
impl From<LambdaError> for Rc<CatchableError> {
|
||||
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
|
||||
/// 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 {
|
||||
impl ToErrorCode for Rc<CatchableError> {
|
||||
fn to_error_code(&self) -> i64 {
|
||||
const EXECUTION_ERRORS_START_ID: i64 = 1000;
|
||||
|
||||
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
|
||||
self.as_ref().to_error_code()
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
impl ToErrorCode for Rc<ExecutionError> {
|
||||
impl ToErrorCode for CatchableError {
|
||||
fn to_error_code(&self) -> i64 {
|
||||
const EXECUTION_ERRORS_START_ID: i64 = 1000;
|
||||
|
||||
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
|
||||
use crate::utils::CATCHABLE_ERRORS_START_ID;
|
||||
crate::generate_to_error_code!(self, CatchableError, CATCHABLE_ERRORS_START_ID)
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,11 +105,11 @@ macro_rules! 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.
|
||||
/// Print log if this is joinable error type.
|
||||
fn is_joinable(&self) -> bool {
|
||||
use ExecutionError::*;
|
||||
use CatchableError::*;
|
||||
|
||||
match self {
|
||||
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.
|
||||
*/
|
||||
|
||||
/// This trait is intended to differentiate between catchable and non-catchable error types.
|
||||
/// Errors of the first type could be caught by xor, the second couldn't and should stop
|
||||
/// AIR execution. This is needed to prevent some malicious data merging and manage
|
||||
/// prev_data always in a valid state.
|
||||
pub(crate) trait Catchable {
|
||||
/// Return true, if error is catchable.
|
||||
fn is_catchable(&self) -> bool;
|
||||
}
|
||||
mod catchable_errors;
|
||||
mod execution_errors;
|
||||
mod joinable;
|
||||
mod uncatchable_errors;
|
||||
|
||||
pub use catchable_errors::CatchableError;
|
||||
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 crate::execution_step::ExecutionError;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
@ -28,7 +28,7 @@ use std::rc::Rc;
|
||||
/// This struct is intended to track the last arisen error.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct LastErrorDescriptor {
|
||||
pub(crate) error: Rc<ExecutionError>,
|
||||
pub(crate) error: Rc<CatchableError>,
|
||||
pub(crate) instruction: String,
|
||||
pub(crate) peer_id: String,
|
||||
pub(crate) tetraplet: Option<RSecurityTetraplet>,
|
||||
@ -70,7 +70,7 @@ impl<'s> LastErrorWithTetraplet {
|
||||
|
||||
impl LastErrorDescriptor {
|
||||
pub(crate) fn new(
|
||||
error: Rc<ExecutionError>,
|
||||
error: Rc<CatchableError>,
|
||||
instruction: String,
|
||||
peer_id: String,
|
||||
tetraplet: Option<RSecurityTetraplet>,
|
||||
|
@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::exec_err;
|
||||
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::FoldState;
|
||||
use crate::execution_step::ValueAggregate;
|
||||
@ -87,7 +86,7 @@ impl<'i> Scalars<'i> {
|
||||
}
|
||||
Occupied(entry) => {
|
||||
if !shadowing_allowed {
|
||||
return exec_err!(ExecutionError::MultipleVariablesFound(entry.key().clone()));
|
||||
return Err(UncatchableError::MultipleVariablesFound(entry.key().clone()).into());
|
||||
}
|
||||
|
||||
let values = entry.into_mut();
|
||||
@ -115,9 +114,7 @@ impl<'i> Scalars<'i> {
|
||||
entry.insert(fold_state);
|
||||
Ok(())
|
||||
}
|
||||
Occupied(entry) => {
|
||||
exec_err!(ExecutionError::MultipleIterableValues(entry.key().clone()))
|
||||
}
|
||||
Occupied(entry) => Err(UncatchableError::MultipleIterableValues(entry.key().clone()).into()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,13 +132,13 @@ impl<'i> Scalars<'i> {
|
||||
.rev()
|
||||
.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>> {
|
||||
self.iterable_values
|
||||
.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>> {
|
||||
@ -149,7 +146,7 @@ impl<'i> Scalars<'i> {
|
||||
let iterable_value = self.iterable_values.get(name);
|
||||
|
||||
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)),
|
||||
(Err(_), Some(iterable_value)) => Ok(ScalarRef::IterableValue(iterable_value)),
|
||||
(Ok(_), Some(_)) => unreachable!("this is checked on the parsing stage"),
|
||||
|
@ -189,7 +189,7 @@ fn find_closest<'d>(
|
||||
) -> Option<&'d RefCell<Stream>> {
|
||||
// descriptors are placed in a order of decreasing scopes, so it's enough to get the latest suitable
|
||||
for descriptor in descriptors.rev() {
|
||||
if descriptor.span.contains(position) {
|
||||
if descriptor.span.contains_position(position) {
|
||||
return Some(&descriptor.stream);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ use super::utils::*;
|
||||
use super::LambdaError;
|
||||
use crate::execution_step::ExecutionCtx;
|
||||
use crate::execution_step::ExecutionResult;
|
||||
use crate::lambda_to_execution_error;
|
||||
use crate::JValue;
|
||||
use crate::LambdaAST;
|
||||
|
||||
@ -39,19 +40,18 @@ pub(crate) fn select_from_stream<'value, 'i>(
|
||||
let idx = match prefix {
|
||||
ArrayAccess { idx } => *idx,
|
||||
FieldAccessByName { field_name } => {
|
||||
return Err(LambdaError::FieldAccessorAppliedToStream {
|
||||
return lambda_to_execution_error!(Err(LambdaError::FieldAccessorAppliedToStream {
|
||||
field_name: field_name.to_string(),
|
||||
}
|
||||
.into());
|
||||
}));
|
||||
}
|
||||
_ => unreachable!("should not execute if parsing succeeded. QED."),
|
||||
};
|
||||
|
||||
let stream_size = stream.len();
|
||||
let value = stream
|
||||
let value = lambda_to_execution_error!(stream
|
||||
.peekable()
|
||||
.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 select_result = StreamSelectResult::new(result, idx);
|
||||
@ -66,14 +66,14 @@ pub(crate) fn select<'value, 'accessor, 'i>(
|
||||
for accessor in lambda {
|
||||
match accessor {
|
||||
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 } => {
|
||||
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 } => {
|
||||
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."),
|
||||
}
|
||||
|
@ -18,8 +18,9 @@ use crate::JValue;
|
||||
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
/// Describes errors related to applying lambdas to values.
|
||||
#[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")]
|
||||
StreamNotHaveEnoughValues { stream_size: usize, idx: u32 },
|
||||
|
||||
@ -36,12 +37,12 @@ pub(crate) enum LambdaError {
|
||||
#[error("value '{value}' does not contain element for idx = '{idx}'")]
|
||||
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}'")]
|
||||
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`")]
|
||||
IndexAccessNotU32 { accessor: serde_json::Number },
|
||||
|
||||
|
@ -18,8 +18,20 @@ mod applier;
|
||||
mod errors;
|
||||
mod utils;
|
||||
|
||||
pub use errors::LambdaError;
|
||||
|
||||
pub(crate) type LambdaResult<T> = std::result::Result<T, LambdaError>;
|
||||
|
||||
pub(crate) use applier::select;
|
||||
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,
|
||||
) -> LambdaResult<&'value JValue> {
|
||||
match jvalue {
|
||||
JValue::Object(values_map) => {
|
||||
values_map
|
||||
.get(field_name)
|
||||
.ok_or_else(|| LambdaError::JValueNotContainSuchField {
|
||||
value: jvalue.clone(),
|
||||
field_name: field_name.to_string(),
|
||||
})
|
||||
}
|
||||
JValue::Object(values_map) => values_map
|
||||
.get(field_name)
|
||||
.ok_or_else(|| LambdaError::ValueNotContainSuchField {
|
||||
value: jvalue.clone(),
|
||||
field_name: field_name.to_string(),
|
||||
}),
|
||||
_ => Err(LambdaError::FieldAccessorNotMatchValue {
|
||||
value: jvalue.clone(),
|
||||
field_name: field_name.to_string(),
|
||||
|
@ -21,14 +21,23 @@ pub(crate) mod execution_context;
|
||||
mod lambda_applier;
|
||||
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::FoldState;
|
||||
pub(super) use boxed_value::Generation;
|
||||
pub(super) use boxed_value::ScalarRef;
|
||||
pub(super) use boxed_value::Stream;
|
||||
pub(super) use boxed_value::ValueAggregate;
|
||||
pub(crate) use errors::Catchable;
|
||||
pub(super) use errors::ExecutionError;
|
||||
pub(crate) use errors::Joinable;
|
||||
pub(crate) use execution_context::ExecutionCtx;
|
||||
|
||||
@ -37,13 +46,6 @@ pub(crate) use air_trace_handler::TraceHandler;
|
||||
use std::cell::RefCell;
|
||||
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 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 {
|
||||
fn to_error_code(&self) -> i64 {
|
||||
const FAREWELL_ERRORS_START_ID: i64 = 20000;
|
||||
|
||||
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
|
||||
use crate::utils::FAREWELL_ERRORS_START_ID;
|
||||
crate::generate_to_error_code!(self, FarewellError, FAREWELL_ERRORS_START_ID)
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,8 @@
|
||||
mod errors;
|
||||
mod outcome;
|
||||
|
||||
pub(crate) use errors::FarewellError;
|
||||
pub use errors::FarewellError;
|
||||
|
||||
pub(crate) use outcome::from_execution_error;
|
||||
pub(crate) use outcome::from_success_result;
|
||||
pub(crate) use outcome::from_uncatchable_error;
|
||||
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#![allow(improper_ctypes)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![deny(
|
||||
dead_code,
|
||||
@ -36,8 +35,14 @@ pub use air_interpreter_interface::InterpreterOutcome;
|
||||
pub use air_interpreter_interface::RunParameters;
|
||||
pub use air_interpreter_interface::INTERPRETER_SUCCESS;
|
||||
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::SecurityTetraplet;
|
||||
pub use preparation_step::PreparationError;
|
||||
pub use utils::ToErrorCode;
|
||||
|
||||
pub use crate::runner::execute_air;
|
||||
|
||||
@ -55,6 +60,5 @@ pub mod parser {
|
||||
}
|
||||
|
||||
pub(crate) type JValue = serde_json::Value;
|
||||
pub(crate) use utils::ToErrorCode;
|
||||
|
||||
use air_lambda_parser::LambdaAST;
|
||||
|
@ -43,13 +43,7 @@ pub enum PreparationError {
|
||||
|
||||
impl ToErrorCode for PreparationError {
|
||||
fn to_error_code(&self) -> i64 {
|
||||
const PREPARATION_ERRORS_START_ID: i64 = 1;
|
||||
|
||||
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
|
||||
use crate::utils::PREPARATION_ERROR_START_ID;
|
||||
crate::generate_to_error_code!(self, PreparationError, PREPARATION_ERROR_START_ID)
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
mod errors;
|
||||
mod preparation;
|
||||
|
||||
pub(crate) use errors::PreparationError;
|
||||
pub use errors::PreparationError;
|
||||
|
||||
pub(crate) use preparation::prepare;
|
||||
pub(crate) use preparation::PreparationDescriptor;
|
||||
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::execution_step::Catchable;
|
||||
use crate::execution_step::ExecutableInstruction;
|
||||
use crate::farewell_step as farewell;
|
||||
use crate::preparation_step::prepare;
|
||||
@ -58,8 +57,8 @@ fn execute_air_impl(
|
||||
mut trace_handler,
|
||||
air,
|
||||
} = match prepare(&prev_data, &data, air.as_str(), &call_results, params) {
|
||||
Ok(desc) => desc,
|
||||
// return the initial data in case of errors
|
||||
Ok(descriptor) => descriptor,
|
||||
// return the prev data in case of errors
|
||||
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),
|
||||
// return new collected trace in case of errors
|
||||
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)),
|
||||
}
|
||||
}
|
||||
|
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.
|
||||
*/
|
||||
|
||||
mod error_codes;
|
||||
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.
|
||||
*/
|
||||
|
||||
pub(crate) trait ToErrorCode {
|
||||
pub trait ToErrorCode {
|
||||
fn to_error_code(&self) -> i64;
|
||||
}
|
||||
|
||||
/*
|
||||
use concat_idents::concat_idents;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! generate_to_error_code {
|
||||
($error_type:ident, $start_id: ident) => {
|
||||
const PREPARATION_ERRORS_START_ID: u32 = $start_id;
|
||||
($self: expr, $error_type:ident, $start_id: expr) => {
|
||||
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 actual_error_type = PreparationErrorDiscriminants::from(self);
|
||||
let mut errors = error_discriminant::iter();
|
||||
let actual_error_type = error_discriminant::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
|
||||
// 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;
|
||||
error_start_id + enum_variant_position
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -14,8 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::UncatchableError;
|
||||
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).
|
||||
// Additionally, check that empty string for data does the same as empty call path.
|
||||
#[test]
|
||||
@ -98,18 +102,21 @@ fn variables() {
|
||||
// Check that duplicate variables are impossible.
|
||||
#[test]
|
||||
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
|
||||
(call "some_peer_id" ("some_service_id" "local_fn_name") [] modules)
|
||||
(call "some_peer_id" ("some_service_id" "local_fn_name") [] modules)
|
||||
(call "{peer_id}" ("some_service_id" "local_fn_name") [] {variable_name})
|
||||
(call "{peer_id}" ("some_service_id" "local_fn_name") [] {variable_name})
|
||||
)
|
||||
"#;
|
||||
"#);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -14,11 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::CatchableError;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
use fstrings::f;
|
||||
use fstrings::format_args_f;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
fn fail_with_last_error() {
|
||||
let local_peer_id = "local_peer_id";
|
||||
@ -32,11 +35,10 @@ fn fail_with_last_error() {
|
||||
)"#);
|
||||
|
||||
let result = call_vm!(vm, "", script, "", "");
|
||||
assert_eq!(result.ret_code, 1000);
|
||||
assert_eq!(
|
||||
result.error_message,
|
||||
r#"Local service error, ret_code is 1, error message is '"failed result from fallible_call_service"'"#
|
||||
);
|
||||
|
||||
let expected_error =
|
||||
CatchableError::LocalServiceError(1, Rc::new(r#""failed result from fallible_call_service""#.to_string()));
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -53,11 +55,12 @@ fn fail_with_literals() {
|
||||
);
|
||||
|
||||
let result = call_vm!(vm, "", script, "", "");
|
||||
assert_eq!(result.ret_code, 1012);
|
||||
assert_eq!(
|
||||
result.error_message,
|
||||
"fail with ret_code '1337' and error_message 'error message' is used without corresponding xor"
|
||||
);
|
||||
|
||||
let expected_error = CatchableError::FailWithoutXorError {
|
||||
ret_code: 1337,
|
||||
error_message: "error message".to_string(),
|
||||
};
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[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!(
|
||||
tetraplet_anchor.borrow()[0][0],
|
||||
SecurityTetraplet::new(local_peer_id, fallible_service_id, local_fn_name, "")
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::{PreparationError, ToErrorCode};
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
#[test]
|
||||
@ -157,7 +158,8 @@ fn inner_fold_with_same_iterator() {
|
||||
|
||||
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]
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::CatchableError;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
#[test]
|
||||
@ -163,8 +164,9 @@ fn match_with_equal_numbers() {
|
||||
(null)
|
||||
)";
|
||||
|
||||
let result = checked_call_vm!(vm, "asd", script, "", "");
|
||||
assert_eq!(result.ret_code, 0);
|
||||
let result = call_vm!(vm, "asd", script, "", "");
|
||||
|
||||
assert!(is_interpreter_succeded(&result));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -192,11 +194,13 @@ fn match_without_xor() {
|
||||
let result = call_vm!(set_variable_vm, "", &script, "", "");
|
||||
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);
|
||||
|
||||
assert_eq!(result.ret_code, 1010);
|
||||
let expected_error = CatchableError::MatchWithoutXorError;
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::CatchableError;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
#[test]
|
||||
@ -143,11 +144,13 @@ fn mismatch_without_xor() {
|
||||
let result = call_vm!(set_variable_vm, "asd", &script, "", "");
|
||||
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);
|
||||
|
||||
assert_eq!(result.ret_code, 1011);
|
||||
let expected_error = CatchableError::MismatchWithoutXorError;
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -183,6 +186,5 @@ fn mismatch_with_two_xors() {
|
||||
let mut actual_trace = trace_from_result(&result);
|
||||
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);
|
||||
}
|
||||
|
@ -14,8 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::UncatchableError;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
use fstrings::f;
|
||||
use fstrings::format_args_f;
|
||||
|
||||
#[test]
|
||||
fn xor() {
|
||||
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 local_peer_id = "local_peer_id";
|
||||
let mut vm = create_avm(echo_call_service(), local_peer_id);
|
||||
|
||||
let some_string = String::from("some_string");
|
||||
let expected_string = String::from("expected_string");
|
||||
let script = format!(
|
||||
r#"
|
||||
let some_string = "some_string";
|
||||
let expected_string = "expected_string";
|
||||
let variable_name = "result_1";
|
||||
let script = f!(r#"
|
||||
(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
|
||||
(call "{1}" ("service_id_1" "local_fn_name") [""] result_1)
|
||||
(call "{1}" ("service_id_2" "local_fn_name") ["{3}"] result_2)
|
||||
(call "{local_peer_id}" ("service_id_1" "local_fn_name") [""] {variable_name})
|
||||
(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 = checked_call_vm!(vm, "asd", script, "", result.data);
|
||||
let result = call_vm!(set_variables_vm, "asd", &script, "", "");
|
||||
|
||||
let actual_trace = trace_from_result(&result);
|
||||
let expected_trace = vec![
|
||||
executed_state::scalar_string(some_string),
|
||||
executed_state::scalar_string(expected_string),
|
||||
];
|
||||
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
let expected_error = UncatchableError::MultipleVariablesFound(variable_name.to_string());
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::PreparationError;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
#[test]
|
||||
@ -160,5 +161,8 @@ fn invalid_air() {
|
||||
let script = r#"(seq )"#;
|
||||
|
||||
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!(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!(
|
||||
closure_call_args.service_id_var,
|
||||
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!(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!(
|
||||
closure_call_args.service_id_var,
|
||||
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!(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![])));
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::CatchableError;
|
||||
use air::LambdaError;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
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 actual_trace = trace_from_result(&result);
|
||||
|
||||
assert_eq!(result.ret_code, 0);
|
||||
assert_eq!(actual_trace.len(), 3);
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
@ -135,17 +135,17 @@ fn wait_on_empty_stream_json_path() {
|
||||
|
||||
#[test]
|
||||
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!({
|
||||
"err_msg": "",
|
||||
"is_authenticated": 1,
|
||||
"ret_code": 0,
|
||||
"is_authenticated": 1i32,
|
||||
"ret_code": 0i32,
|
||||
});
|
||||
|
||||
let variables = maplit::hashmap!(
|
||||
"array".to_string() => array,
|
||||
"object".to_string() => object,
|
||||
"array".to_string() => array.clone(),
|
||||
"object".to_string() => object.clone(),
|
||||
);
|
||||
|
||||
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 result = call_vm!(set_variable_vm, init_peer_id, &script, "", "");
|
||||
let array_result = call_vm!(array_consumer, init_peer_id, &script, "", result.data.clone());
|
||||
assert_eq!(array_result.ret_code, 1003);
|
||||
assert_eq!(
|
||||
array_result.error_message,
|
||||
r#"value '[1,2,3,4,5]' does not contain element for idx = '5'"#
|
||||
);
|
||||
|
||||
let expected_error =
|
||||
CatchableError::LambdaApplierError(LambdaError::ValueNotContainSuchArrayIdx { value: array, idx: 5 });
|
||||
assert!(check_error(&array_result, expected_error));
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
@ -191,11 +190,13 @@ fn dont_wait_on_json_path_on_scalars() {
|
||||
let init_peer_id = "asd";
|
||||
let result = call_vm!(set_variable_vm, init_peer_id, &script, "", "");
|
||||
let object_result = call_vm!(object_consumer, init_peer_id, script, "", result.data);
|
||||
assert_eq!(object_result.ret_code, 1003);
|
||||
assert_eq!(
|
||||
object_result.error_message,
|
||||
r#"value '{"err_msg":"","is_authenticated":1,"ret_code":0}' does not contain element with field name = 'non_exist_path'"#
|
||||
);
|
||||
|
||||
let expected_error = CatchableError::LambdaApplierError(LambdaError::ValueNotContainSuchField {
|
||||
value: object,
|
||||
field_name: "non_exist_path".to_string(),
|
||||
});
|
||||
|
||||
assert!(check_error(&object_result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::CatchableError;
|
||||
use air::LambdaError;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
use fstrings::f;
|
||||
@ -27,20 +29,22 @@ fn lambda_not_allowed_for_non_objects_and_arrays() {
|
||||
let local_peer_id = "local_peer_id";
|
||||
let mut local_vm = create_avm(echo_call_service(), local_peer_id);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
let some_string = "some_string";
|
||||
let script = f!(r#"
|
||||
(seq
|
||||
(call "{0}" ("" "") ["some_string"] string_variable)
|
||||
(call "{1}" ("" "") [string_variable.$.some_lambda])
|
||||
(call "{set_variable_peer_id}" ("" "") ["{some_string}"] string_variable)
|
||||
(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 = 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]
|
||||
|
@ -14,8 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::UncatchableError;
|
||||
use air_test_utils::prelude::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use air_trace_handler::TraceHandlerError;
|
||||
use air_trace_handler::{CallResultError, MergeError};
|
||||
|
||||
#[test]
|
||||
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 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 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_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);
|
||||
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))
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
pub fn name(&self) -> &'i str {
|
||||
match self {
|
||||
Variable::Scalar(scalar) => scalar.name,
|
||||
Variable::Stream(stream) => stream.name,
|
||||
@ -113,7 +113,7 @@ impl<'i> VariableWithLambda<'i> {
|
||||
Self::Stream(StreamWithLambda::new(name, Some(lambda), position))
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
pub fn name(&self) -> &'i str {
|
||||
match self {
|
||||
VariableWithLambda::Scalar(scalar) => scalar.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, _) => {
|
||||
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")]
|
||||
IteratorRestrictionNotAllowed(usize, usize, String),
|
||||
|
||||
#[error("multiple iterable values found for iterable name '{2}'")]
|
||||
MultipleIterableValues(usize, usize, String),
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for ParserError {
|
||||
|
@ -17,7 +17,7 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Span {
|
||||
pub left: usize,
|
||||
pub right: usize,
|
||||
@ -28,7 +28,35 @@ impl Span {
|
||||
Self { left, right }
|
||||
}
|
||||
|
||||
pub fn contains(&self, position: usize) -> bool {
|
||||
pub fn contains_position(&self, position: usize) -> bool {
|
||||
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(..)));
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn parse_fold() {
|
||||
let source_code = r#"
|
||||
|
@ -25,6 +25,7 @@ use lalrpop_util::ParseError;
|
||||
|
||||
use multimap::MultiMap;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Intermediate implementation of variable validator.
|
||||
///
|
||||
@ -36,10 +37,10 @@ use std::collections::HashMap;
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct VariableValidator<'i> {
|
||||
/// 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.
|
||||
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.
|
||||
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) {
|
||||
self.check_for_non_iterators
|
||||
.push((variable_name(&new.variable), span));
|
||||
.push((new.variable.name(), span));
|
||||
// new defines a new variable
|
||||
self.met_variable_definition(&new.variable, span);
|
||||
}
|
||||
@ -120,7 +121,7 @@ impl<'i> VariableValidator<'i> {
|
||||
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();
|
||||
for (name, span) in self.unresolved_variables.iter() {
|
||||
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
|
||||
}
|
||||
|
||||
@ -169,8 +183,7 @@ impl<'i> VariableValidator<'i> {
|
||||
}
|
||||
|
||||
fn met_variable_wl(&mut self, variable: &VariableWithLambda<'i>, span: Span) {
|
||||
let name = variable_wl_name(variable);
|
||||
self.met_variable_name(name, span);
|
||||
self.met_variable_name(variable.name(), 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 {
|
||||
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 {
|
||||
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,
|
||||
None => return false,
|
||||
};
|
||||
@ -195,14 +208,13 @@ impl<'i> VariableValidator<'i> {
|
||||
}
|
||||
|
||||
fn met_variable_definition(&mut self, variable: &Variable<'i>, span: Span) {
|
||||
let name = variable_name(variable);
|
||||
self.met_variable_name_definition(name, span);
|
||||
self.met_variable_name_definition(variable.name(), span);
|
||||
}
|
||||
|
||||
fn met_variable_name_definition(&mut self, name: &'i str, span: Span) {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
match self.met_variables.entry(name) {
|
||||
match self.met_variable_definitions.entry(name) {
|
||||
Entry::Occupied(occupied) => {
|
||||
if occupied.get() > &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.
|
||||
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,
|
||||
None => return false,
|
||||
};
|
||||
@ -239,25 +251,7 @@ impl<'i> VariableValidator<'i> {
|
||||
}
|
||||
|
||||
fn met_iterator_definition(&mut self, iterator: &Scalar<'i>, span: Span) {
|
||||
self.met_iterators.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)
|
||||
}
|
||||
self.met_iterator_definitions.insert(iterator.name, span);
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,6 +267,7 @@ fn add_to_errors<'err, 'i>(
|
||||
Token::New => {
|
||||
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),
|
||||
};
|
||||
let error = ParseError::User { error };
|
||||
@ -286,17 +281,3 @@ fn add_to_errors<'err, 'i>(
|
||||
|
||||
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!("]");
|
||||
}
|
||||
|
||||
#[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