mirror of
https://github.com/fluencelabs/aquavm
synced 2025-03-15 20:40:50 +00:00
Implement fail with scalars (#205)
This commit is contained in:
parent
91021d8b40
commit
e6193ea4de
@ -27,7 +27,7 @@ pub(super) fn apply_to_arg(
|
||||
use ast::ApArgument::*;
|
||||
|
||||
let result = match argument {
|
||||
InitPeerId => apply_const(exec_ctx.init_peer_id.clone(), exec_ctx, trace_ctx),
|
||||
InitPeerId => apply_const(exec_ctx.init_peer_id.as_str(), exec_ctx, trace_ctx),
|
||||
LastError(error_accessor) => apply_last_error(error_accessor, exec_ctx, trace_ctx)?,
|
||||
Literal(value) => apply_const(*value, exec_ctx, trace_ctx),
|
||||
Number(value) => apply_const(value, exec_ctx, trace_ctx),
|
||||
@ -41,7 +41,7 @@ pub(super) fn apply_to_arg(
|
||||
|
||||
fn apply_const(value: impl Into<JValue>, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler) -> ValueAggregate {
|
||||
let value = Rc::new(value.into());
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.init_peer_id.clone());
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.init_peer_id.as_ref());
|
||||
let tetraplet = Rc::new(RefCell::new(tetraplet));
|
||||
|
||||
ValueAggregate::new(value, tetraplet, trace_ctx.trace_pos())
|
||||
|
@ -73,10 +73,10 @@ fn set_last_error<'i>(
|
||||
current_peer_id
|
||||
);
|
||||
|
||||
let _ = exec_ctx.last_error_descriptor.try_to_set_from_ingredients(
|
||||
let _ = exec_ctx.last_error_descriptor.try_to_set_from_error(
|
||||
catchable_error.as_ref(),
|
||||
call.to_string(),
|
||||
current_peer_id,
|
||||
&call.to_string(),
|
||||
¤t_peer_id,
|
||||
tetraplet,
|
||||
);
|
||||
ExecutionError::Catchable(catchable_error)
|
||||
|
@ -48,7 +48,7 @@ fn resolve_to_string<'i>(value: &ast::CallInstrValue<'i>, ctx: &ExecutionCtx<'i>
|
||||
use ast::CallInstrValue::*;
|
||||
|
||||
let resolved = match value {
|
||||
InitPeerId => ctx.init_peer_id.clone(),
|
||||
InitPeerId => ctx.init_peer_id.to_string(),
|
||||
Literal(value) => value.to_string(),
|
||||
Variable(variable) => {
|
||||
let (resolved, _) = resolve_ast_variable_wl(variable, ctx)?;
|
||||
|
@ -80,7 +80,7 @@ fn compare_matchable<'ctx>(
|
||||
match matchable {
|
||||
InitPeerId => {
|
||||
let init_peer_id = exec_ctx.init_peer_id.clone();
|
||||
let jvalue = init_peer_id.into();
|
||||
let jvalue = init_peer_id.as_str().into();
|
||||
Ok(comparator(Cow::Owned(jvalue)))
|
||||
}
|
||||
LastError(error_accessor) => {
|
||||
|
@ -17,6 +17,8 @@
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionResult;
|
||||
use super::TraceHandler;
|
||||
use crate::execution_step::execution_context::check_error_object;
|
||||
use crate::execution_step::resolver::resolve_ast_scalar_wl;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::execution_step::LastError;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
@ -24,6 +26,7 @@ use crate::log_instruction;
|
||||
use crate::ExecutionError;
|
||||
use crate::JValue;
|
||||
|
||||
use air_parser::ast;
|
||||
use air_parser::ast::Fail;
|
||||
use polyplets::SecurityTetraplet;
|
||||
|
||||
@ -35,6 +38,7 @@ impl<'i> super::ExecutableInstruction<'i> for Fail<'i> {
|
||||
log_instruction!(fail, exec_ctx, trace_ctx);
|
||||
|
||||
match self {
|
||||
Fail::Scalar(scalar) => fail_with_scalar(scalar, exec_ctx),
|
||||
&Fail::Literal {
|
||||
ret_code,
|
||||
error_message,
|
||||
@ -45,25 +49,32 @@ impl<'i> super::ExecutableInstruction<'i> for Fail<'i> {
|
||||
}
|
||||
}
|
||||
|
||||
fn fail_with_scalar<'i>(scalar: &ast::ScalarWithLambda<'i>, exec_ctx: &mut ExecutionCtx<'i>) -> ExecutionResult<()> {
|
||||
let (value, mut tetraplet) = resolve_ast_scalar_wl(scalar, exec_ctx)?;
|
||||
// tetraplets always have one element here and it'll be refactored after boxed value
|
||||
let tetraplet = tetraplet.remove(0);
|
||||
check_error_object(&value).map_err(CatchableError::InvalidLastErrorObjectError)?;
|
||||
|
||||
fail_with_error_object(exec_ctx, Rc::new(value), Some(tetraplet))
|
||||
}
|
||||
|
||||
fn fail_with_literals<'i>(
|
||||
ret_code: i64,
|
||||
error_code: i64,
|
||||
error_message: &str,
|
||||
fail: &Fail<'_>,
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<()> {
|
||||
// TODO: decouple error object creation into a separate function
|
||||
let error_object = serde_json::json!({
|
||||
"error_code": ret_code,
|
||||
"message": error_message,
|
||||
"instruction": fail.to_string(),
|
||||
});
|
||||
let error_object = Rc::new(error_object);
|
||||
let error_object = crate::execution_step::execution_context::error_from_raw_fields(
|
||||
error_code,
|
||||
error_message,
|
||||
&fail.to_string(),
|
||||
exec_ctx.init_peer_id.as_ref(),
|
||||
);
|
||||
|
||||
// TODO: wrap exec.init_peer_id in Rc
|
||||
let literal_tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.init_peer_id.clone());
|
||||
let literal_tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.init_peer_id.as_ref());
|
||||
let literal_tetraplet = Rc::new(RefCell::new(literal_tetraplet));
|
||||
|
||||
fail_with_error_object(exec_ctx, error_object, Some(literal_tetraplet))
|
||||
fail_with_error_object(exec_ctx, Rc::new(error_object), Some(literal_tetraplet))
|
||||
}
|
||||
|
||||
fn fail_with_last_error(exec_ctx: &mut ExecutionCtx<'_>) -> ExecutionResult<()> {
|
||||
|
@ -48,10 +48,11 @@ macro_rules! execute {
|
||||
($self:expr, $instr:expr, $exec_ctx:ident, $trace_ctx:ident) => {{
|
||||
match $instr.execute($exec_ctx, $trace_ctx) {
|
||||
Err(e) => {
|
||||
$exec_ctx.last_error_descriptor.try_to_set_from_ingredients(
|
||||
$exec_ctx.last_error_descriptor.try_to_set_from_error(
|
||||
&e,
|
||||
$instr.to_string(),
|
||||
$exec_ctx.current_peer_id.to_string(),
|
||||
// TODO: avoid excess copying here
|
||||
&$instr.to_string(),
|
||||
$exec_ctx.current_peer_id.as_ref(),
|
||||
None,
|
||||
);
|
||||
Err(e)
|
||||
|
@ -38,10 +38,10 @@ impl<'i> ExecutableInstruction<'i> for Par<'i> {
|
||||
trace_to_exec_err!(trace_ctx.meet_par_start(), self)?;
|
||||
|
||||
// execute a left subtree of par
|
||||
let left_result = execute_subtree(&self, exec_ctx, trace_ctx, &mut completeness_updater, SubtreeType::Left)?;
|
||||
let left_result = execute_subtree(self, exec_ctx, trace_ctx, &mut completeness_updater, SubtreeType::Left)?;
|
||||
|
||||
// execute a right subtree of par
|
||||
let right_result = execute_subtree(&self, exec_ctx, trace_ctx, &mut completeness_updater, SubtreeType::Right)?;
|
||||
let right_result = execute_subtree(self, exec_ctx, trace_ctx, &mut completeness_updater, SubtreeType::Right)?;
|
||||
|
||||
completeness_updater.set_completeness(exec_ctx);
|
||||
prepare_par_result(left_result, right_result, exec_ctx)
|
||||
|
@ -17,6 +17,7 @@
|
||||
use super::Joinable;
|
||||
use super::LastErrorAffectable;
|
||||
use super::Stream;
|
||||
use crate::execution_step::execution_context::LastErrorObjectError;
|
||||
use crate::execution_step::lambda_applier::LambdaError;
|
||||
use crate::JValue;
|
||||
use crate::ToErrorCode;
|
||||
@ -77,6 +78,10 @@ pub enum CatchableError {
|
||||
/// 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),
|
||||
|
||||
/// This error type is produced by a fail instruction that tries to throw a scalar that have inappropriate type.
|
||||
#[error(transparent)]
|
||||
InvalidLastErrorObjectError(#[from] LastErrorObjectError),
|
||||
}
|
||||
|
||||
impl From<LambdaError> for Rc<CatchableError> {
|
||||
|
@ -40,7 +40,7 @@ pub(crate) struct ExecutionCtx<'i> {
|
||||
pub(crate) current_peer_id: Rc<String>,
|
||||
|
||||
/// PeerId of a peer send this AIR script.
|
||||
pub(crate) init_peer_id: String,
|
||||
pub(crate) init_peer_id: Rc<String>,
|
||||
|
||||
/// Last error produced by local service.
|
||||
/// None means that there weren't any error.
|
||||
@ -78,7 +78,7 @@ impl<'i> ExecutionCtx<'i> {
|
||||
|
||||
Self {
|
||||
current_peer_id,
|
||||
init_peer_id,
|
||||
init_peer_id: Rc::new(init_peer_id),
|
||||
subtree_complete: true,
|
||||
last_call_request_id,
|
||||
call_results,
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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::JValue;
|
||||
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
/// Describes errors related to converting a scalar into error object.
|
||||
#[derive(Debug, Clone, ThisError)]
|
||||
pub enum LastErrorObjectError {
|
||||
#[error("scalar should have an object type to be converted into error object, but '{0}' doesn't have")]
|
||||
ScalarMustBeObject(JValue),
|
||||
|
||||
#[error("scalar '{scalar}' must have field with name '{field_name}'")]
|
||||
ScalarMustContainField { scalar: JValue, field_name: &'static str },
|
||||
|
||||
#[error("{field_name} of scalar '{scalar}' must have {expected_type} type")]
|
||||
ScalarFieldIsWrongType {
|
||||
scalar: JValue,
|
||||
field_name: &'static str,
|
||||
expected_type: &'static str,
|
||||
},
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::LastErrorObjectError;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::JValue;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
pub const ERROR_CODE_FIELD_NAME: &str = "error_code";
|
||||
pub const MESSAGE_FIELD_NAME: &str = "message";
|
||||
pub const INSTRUCTION_FIELD_NAME: &str = "instruction";
|
||||
pub const PEER_ID_FIELD_NAME: &str = "peer_id";
|
||||
|
||||
/// This struct is intended to track the last arisen error.
|
||||
/// LastError is essentially a scalar value with support of lambda expressions.
|
||||
/// The only differences from a scalar are
|
||||
/// - it's accessed by %last_error% literal
|
||||
/// - if it's unset before the usage, JValue::Null will be used without join behaviour
|
||||
/// - it's a global scalar, meaning that fold and new scopes doesn't apply for it
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LastError {
|
||||
/// Error object that represents the last occurred error.
|
||||
pub error: Rc<JValue>,
|
||||
|
||||
/// Tetraplet that identify host where the error occurred.
|
||||
pub tetraplet: Option<RSecurityTetraplet>,
|
||||
}
|
||||
|
||||
pub(crate) fn error_from_raw_fields(error_code: i64, error_message: &str, instruction: &str, peer_id: &str) -> JValue {
|
||||
serde_json::json!({
|
||||
ERROR_CODE_FIELD_NAME: error_code,
|
||||
MESSAGE_FIELD_NAME: error_message,
|
||||
INSTRUCTION_FIELD_NAME: instruction,
|
||||
PEER_ID_FIELD_NAME: peer_id,
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks that a scalar is a value of an object types that contains at least two fields:
|
||||
/// - error_code
|
||||
/// - message
|
||||
pub(crate) fn check_error_object(scalar: &JValue) -> Result<(), LastErrorObjectError> {
|
||||
let fields = match scalar {
|
||||
JValue::Object(fields) => fields,
|
||||
_ => return Err(LastErrorObjectError::ScalarMustBeObject(scalar.clone())),
|
||||
};
|
||||
|
||||
let check_field = |field_name| {
|
||||
fields
|
||||
.get(field_name)
|
||||
.ok_or_else(|| LastErrorObjectError::ScalarMustContainField {
|
||||
scalar: scalar.clone(),
|
||||
field_name,
|
||||
})
|
||||
};
|
||||
|
||||
let error_code = check_field(ERROR_CODE_FIELD_NAME)?;
|
||||
ensure_jvalue_is_integer(scalar, error_code, ERROR_CODE_FIELD_NAME)?;
|
||||
|
||||
let message = check_field(MESSAGE_FIELD_NAME)?;
|
||||
ensure_jvalue_is_string(scalar, message, MESSAGE_FIELD_NAME)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_jvalue_is_integer(
|
||||
scalar: &JValue,
|
||||
value: &JValue,
|
||||
field_name: &'static str,
|
||||
) -> Result<(), LastErrorObjectError> {
|
||||
match value {
|
||||
JValue::Number(number) if number.is_i64() || number.is_u64() => Ok(()),
|
||||
_ => Err(LastErrorObjectError::ScalarFieldIsWrongType {
|
||||
scalar: scalar.clone(),
|
||||
field_name,
|
||||
expected_type: "integer",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_jvalue_is_string(
|
||||
scalar: &JValue,
|
||||
value: &JValue,
|
||||
field_name: &'static str,
|
||||
) -> Result<(), LastErrorObjectError> {
|
||||
match value {
|
||||
JValue::String(_) => Ok(()),
|
||||
_ => Err(LastErrorObjectError::ScalarFieldIsWrongType {
|
||||
scalar: scalar.clone(),
|
||||
field_name,
|
||||
expected_type: "string",
|
||||
}),
|
||||
}
|
||||
}
|
@ -14,31 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::last_error_definition::error_from_raw_fields;
|
||||
use super::LastError;
|
||||
use crate::execution_step::LastErrorAffectable;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::JValue;
|
||||
use crate::ToErrorCode;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
/// This struct is intended to track the last arisen error.
|
||||
/// LastError is essentially a scalar value with support of lambda expressions.
|
||||
/// The only differences from a scalar are
|
||||
/// - it's accessed by %last_error% literal
|
||||
/// - if it's unset before the usage, JValue::Null will be used without join behaviour
|
||||
/// - it's a global scalar, meaning that fold and new scopes doesn't apply for it
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LastError {
|
||||
/// Error object that represents the last occurred error.
|
||||
pub error: Rc<JValue>,
|
||||
|
||||
/// Tetraplet that identify host where the error occurred.
|
||||
pub tetraplet: Option<RSecurityTetraplet>,
|
||||
}
|
||||
|
||||
pub(crate) struct LastErrorDescriptor {
|
||||
last_error: LastError,
|
||||
|
||||
@ -49,11 +33,11 @@ pub(crate) struct LastErrorDescriptor {
|
||||
}
|
||||
|
||||
impl<'s> LastErrorDescriptor {
|
||||
pub(crate) fn try_to_set_from_ingredients(
|
||||
pub(crate) fn try_to_set_from_error(
|
||||
&mut self,
|
||||
error: &(impl ToString + LastErrorAffectable + ToErrorCode),
|
||||
instruction: impl Into<String>,
|
||||
peer_id: impl Into<String>,
|
||||
error: &(impl LastErrorAffectable + ToErrorCode + ToString),
|
||||
instruction: &str,
|
||||
peer_id: &str,
|
||||
tetraplet: Option<RSecurityTetraplet>,
|
||||
) -> bool {
|
||||
// this check is optimization to prevent creation of an error object in case if error
|
||||
@ -61,13 +45,25 @@ impl<'s> LastErrorDescriptor {
|
||||
if !self.error_can_be_set || !error.affects_last_error() {
|
||||
return false;
|
||||
}
|
||||
let error_object = serde_json::json!({
|
||||
"error_code": error.to_error_code(),
|
||||
"message": error.to_string(),
|
||||
"instruction": instruction.into(),
|
||||
"peer_id": peer_id.into(),
|
||||
});
|
||||
|
||||
self.set_from_ingredients(
|
||||
error.to_error_code(),
|
||||
&error.to_string(),
|
||||
instruction,
|
||||
peer_id,
|
||||
tetraplet,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn set_from_ingredients(
|
||||
&mut self,
|
||||
error_code: i64,
|
||||
error_message: &str,
|
||||
instruction: &str,
|
||||
peer_id: &str,
|
||||
tetraplet: Option<RSecurityTetraplet>,
|
||||
) -> bool {
|
||||
let error_object = error_from_raw_fields(error_code, error_message, instruction, peer_id);
|
||||
self.set_from_error_object(Rc::new(error_object), tetraplet);
|
||||
true
|
||||
}
|
30
air/src/execution_step/execution_context/last_error/mod.rs
Normal file
30
air/src/execution_step/execution_context/last_error/mod.rs
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2020 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod errors;
|
||||
mod last_error_definition;
|
||||
mod last_error_descriptor;
|
||||
|
||||
pub use errors::LastErrorObjectError;
|
||||
pub use last_error_definition::LastError;
|
||||
pub use last_error_definition::ERROR_CODE_FIELD_NAME;
|
||||
pub use last_error_definition::INSTRUCTION_FIELD_NAME;
|
||||
pub use last_error_definition::MESSAGE_FIELD_NAME;
|
||||
pub use last_error_definition::PEER_ID_FIELD_NAME;
|
||||
|
||||
pub(crate) use last_error_definition::check_error_object;
|
||||
pub(crate) use last_error_definition::error_from_raw_fields;
|
||||
pub(crate) use last_error_descriptor::LastErrorDescriptor;
|
@ -15,13 +15,12 @@
|
||||
*/
|
||||
|
||||
mod context;
|
||||
mod last_error_descriptor;
|
||||
mod last_error;
|
||||
mod scalar_variables;
|
||||
mod streams_variables;
|
||||
|
||||
pub use last_error_descriptor::LastError;
|
||||
pub use last_error::*;
|
||||
|
||||
pub(crate) use context::*;
|
||||
pub(crate) use last_error_descriptor::LastErrorDescriptor;
|
||||
pub(crate) use scalar_variables::*;
|
||||
pub(crate) use streams_variables::*;
|
||||
|
@ -24,6 +24,7 @@ mod resolver;
|
||||
pub use errors::CatchableError;
|
||||
pub use errors::ExecutionError;
|
||||
pub use errors::UncatchableError;
|
||||
pub use execution_context::LastErrorObjectError;
|
||||
pub use lambda_applier::LambdaError;
|
||||
|
||||
pub mod errors_prelude {
|
||||
|
@ -39,7 +39,7 @@ pub(crate) fn resolve_to_args<'i>(
|
||||
use ast::Value::*;
|
||||
|
||||
match value {
|
||||
InitPeerId => prepare_const(ctx.init_peer_id.clone(), ctx),
|
||||
InitPeerId => prepare_const(ctx.init_peer_id.as_str(), ctx),
|
||||
LastError(error_accessor) => prepare_last_error(error_accessor, ctx),
|
||||
Literal(value) => prepare_const(value.to_string(), ctx),
|
||||
Boolean(value) => prepare_const(*value, ctx),
|
||||
@ -55,7 +55,7 @@ pub(crate) fn prepare_const(
|
||||
ctx: &ExecutionCtx<'_>,
|
||||
) -> ExecutionResult<(JValue, SecurityTetraplets)> {
|
||||
let jvalue = arg.into();
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.clone());
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.as_ref());
|
||||
let tetraplet = Rc::new(RefCell::new(tetraplet));
|
||||
|
||||
Ok((jvalue, vec![tetraplet]))
|
||||
@ -78,7 +78,7 @@ pub(crate) fn prepare_last_error<'i>(
|
||||
let tetraplets = match tetraplet {
|
||||
Some(tetraplet) => vec![tetraplet.clone()],
|
||||
None => {
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(&ctx.init_peer_id);
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.as_ref());
|
||||
let tetraplet = Rc::new(RefCell::new(tetraplet));
|
||||
vec![tetraplet]
|
||||
}
|
||||
@ -128,6 +128,15 @@ pub(crate) fn resolve_ast_variable_wl<'ctx, 'i>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_ast_scalar_wl<'ctx, 'i>(
|
||||
ast_scalar: &ast::ScalarWithLambda<'_>,
|
||||
exec_ctx: &'ctx ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(JValue, SecurityTetraplets)> {
|
||||
// TODO: wrap lambda path with Rc to make this clone cheaper
|
||||
let variable = ast::VariableWithLambda::Scalar(ast_scalar.clone());
|
||||
resolve_ast_variable_wl(&variable, exec_ctx)
|
||||
}
|
||||
|
||||
pub(crate) fn apply_lambda<'i>(
|
||||
variable: Variable<'_>,
|
||||
lambda: &LambdaAST<'i>,
|
||||
|
@ -38,6 +38,7 @@ 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::LastErrorObjectError;
|
||||
pub use execution_step::UncatchableError;
|
||||
pub use polyplets::ResolvedTriplet;
|
||||
pub use polyplets::SecurityTetraplet;
|
||||
|
@ -58,13 +58,15 @@ fn fail_with_literals() {
|
||||
)"#,
|
||||
);
|
||||
|
||||
let result = call_vm!(vm, "", script, "", "");
|
||||
let init_peer_id = "init_peer_id";
|
||||
let result = call_vm!(vm, init_peer_id, script, "", "");
|
||||
|
||||
let expected_error = CatchableError::UserError {
|
||||
error: rc!(json!( {
|
||||
"error_code": 1337i64,
|
||||
"instruction": "fail 1337 error message",
|
||||
"message": "error message",
|
||||
"peer_id": init_peer_id,
|
||||
})),
|
||||
};
|
||||
assert!(check_error(&result, expected_error));
|
||||
|
@ -14,7 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::{CatchableError, ExecutionError, LambdaError, SecurityTetraplet};
|
||||
use air::CatchableError;
|
||||
use air::ExecutionError;
|
||||
use air::LambdaError;
|
||||
use air::LastErrorObjectError;
|
||||
use air::SecurityTetraplet;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
use fstrings::f;
|
||||
@ -324,3 +328,169 @@ fn last_error_with_par_one_subtree_failed() {
|
||||
});
|
||||
assert_eq!(actual_value, expected_value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fail_with_scalar_rebubble_error() {
|
||||
let fallible_peer_id = "fallible_peer_id";
|
||||
let mut fallible_vm = create_avm(fallible_call_service("fallible_call_service"), fallible_peer_id);
|
||||
|
||||
let script = f!(r#"
|
||||
(xor
|
||||
(call "{fallible_peer_id}" ("fallible_call_service" "") [""])
|
||||
(seq
|
||||
(ap %last_error% scalar)
|
||||
(fail scalar)
|
||||
)
|
||||
)
|
||||
"#);
|
||||
|
||||
let result = call_vm!(fallible_vm, "", &script, "", "");
|
||||
|
||||
let expected_error = CatchableError::UserError {
|
||||
error: rc!(json!({
|
||||
"error_code": 10000i64,
|
||||
"instruction": r#"call "fallible_peer_id" ("fallible_call_service" "") [""] "#,
|
||||
"message": r#"Local service error, ret_code is 1, error message is '"failed result from fallible_call_service"'"#,
|
||||
"peer_id": "fallible_peer_id",
|
||||
})),
|
||||
};
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fail_with_scalar_from_call() {
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
let error_code = 1337;
|
||||
let error_message = "error message";
|
||||
let service_result = json!({"error_code": error_code, "message": error_message});
|
||||
let mut vm = create_avm(set_variable_call_service(service_result), vm_peer_id);
|
||||
|
||||
let script = f!(r#"
|
||||
(seq
|
||||
(call "{vm_peer_id}" ("" "") [""] scalar)
|
||||
(fail scalar)
|
||||
)
|
||||
"#);
|
||||
|
||||
let result = call_vm!(vm, "", &script, "", "");
|
||||
|
||||
let expected_error = CatchableError::UserError {
|
||||
error: rc!(json!({
|
||||
"error_code": error_code,
|
||||
"message": error_message,
|
||||
})),
|
||||
};
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fail_with_scalar_with_lambda_from_call() {
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
let error_code = 1337;
|
||||
let error_message = "error message";
|
||||
let service_result = json!({"error": {"error_code": error_code, "message": error_message}});
|
||||
let mut vm = create_avm(set_variable_call_service(service_result), vm_peer_id);
|
||||
|
||||
let script = f!(r#"
|
||||
(seq
|
||||
(call "{vm_peer_id}" ("" "") [""] scalar)
|
||||
(fail scalar.$.error)
|
||||
)
|
||||
"#);
|
||||
|
||||
let result = call_vm!(vm, "", &script, "", "");
|
||||
|
||||
let expected_error = CatchableError::UserError {
|
||||
error: rc!(json!({
|
||||
"error_code": error_code,
|
||||
"message": error_message,
|
||||
})),
|
||||
};
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fail_with_scalar_from_call_not_enough_fields() {
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
let error_code = 1337;
|
||||
let service_result = json!({ "error_code": error_code });
|
||||
let mut vm = create_avm(set_variable_call_service(service_result.clone()), vm_peer_id);
|
||||
|
||||
let script = f!(r#"
|
||||
(seq
|
||||
(call "{vm_peer_id}" ("" "") [""] scalar)
|
||||
(fail scalar)
|
||||
)
|
||||
"#);
|
||||
|
||||
let result = call_vm!(vm, "", &script, "", "");
|
||||
|
||||
let expected_error = CatchableError::InvalidLastErrorObjectError(LastErrorObjectError::ScalarMustContainField {
|
||||
scalar: service_result,
|
||||
field_name: "message",
|
||||
});
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fail_with_scalar_from_call_not_right_type() {
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
let service_result = json!([]);
|
||||
let mut vm = create_avm(set_variable_call_service(service_result.clone()), vm_peer_id);
|
||||
|
||||
let script = f!(r#"
|
||||
(seq
|
||||
(call "{vm_peer_id}" ("" "") [""] scalar)
|
||||
(fail scalar)
|
||||
)
|
||||
"#);
|
||||
|
||||
let result = call_vm!(vm, "", &script, "", "");
|
||||
|
||||
let expected_error =
|
||||
CatchableError::InvalidLastErrorObjectError(LastErrorObjectError::ScalarMustBeObject(service_result));
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fail_with_scalar_from_call_field_not_right_type() {
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
let service_result = json!({"error_code": "error_code", "message": "error message"});
|
||||
let mut vm = create_avm(set_variable_call_service(service_result.clone()), vm_peer_id);
|
||||
|
||||
let script = f!(r#"
|
||||
(seq
|
||||
(call "{vm_peer_id}" ("" "") [""] scalar)
|
||||
(fail scalar)
|
||||
)
|
||||
"#);
|
||||
|
||||
let result = call_vm!(vm, "", &script, "", "");
|
||||
|
||||
let expected_error = CatchableError::InvalidLastErrorObjectError(LastErrorObjectError::ScalarFieldIsWrongType {
|
||||
scalar: service_result.clone(),
|
||||
field_name: "error_code",
|
||||
expected_type: "integer",
|
||||
});
|
||||
assert!(check_error(&result, expected_error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last_error_with_match() {
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
let mut vm = create_avm(fallible_call_service("fallible_call_service"), vm_peer_id);
|
||||
|
||||
let script = f!(r#"
|
||||
(xor
|
||||
(call "{vm_peer_id}" ("fallible_call_service" "") [""])
|
||||
(match %last_error%.$.error_code 10000
|
||||
(call "{vm_peer_id}" ("" "") [%last_error%])
|
||||
)
|
||||
)
|
||||
"#);
|
||||
|
||||
let result = checked_call_vm!(vm, "asd", &script, "", "");
|
||||
|
||||
let trace = trace_from_result(&result);
|
||||
assert_eq!(trace.len(), 2); // if match works there will be 2 calls in a resulted trace
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ pub struct MisMatch<'i> {
|
||||
/// (fail %last_error%)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub enum Fail<'i> {
|
||||
Scalar(ScalarWithLambda<'i>),
|
||||
Literal {
|
||||
ret_code: i64,
|
||||
error_message: &'i str,
|
||||
|
@ -36,7 +36,7 @@ impl fmt::Display for Instruction<'_> {
|
||||
Next(next) => write!(f, "{}", next),
|
||||
New(new) => write!(f, "{}", new),
|
||||
Null(null) => write!(f, "{}", null),
|
||||
Error => Ok(()),
|
||||
Error => write!(f, "error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,6 +59,7 @@ impl fmt::Display for Ap<'_> {
|
||||
impl fmt::Display for Fail<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Fail::Scalar(scalar) => write!(f, "fail {}", scalar),
|
||||
Fail::Literal {
|
||||
ret_code,
|
||||
error_message,
|
||||
|
@ -136,6 +136,8 @@ ScriptVariable: Variable<'input> = {
|
||||
};
|
||||
|
||||
FailBody: Fail<'input> = {
|
||||
<scalar:Scalar> => Fail::Scalar(ScalarWithLambda::new(scalar.0, None, scalar.1)),
|
||||
<scalar:ScalarWithLambda> => Fail::Scalar(ScalarWithLambda::new(scalar.0, Some(scalar.1), scalar.2)),
|
||||
<ret_code:I64> <error_message:Literal> => Fail::Literal {
|
||||
ret_code,
|
||||
error_message,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -69,6 +69,10 @@ pub(super) fn null() -> Instruction<'static> {
|
||||
Instruction::Null(Null)
|
||||
}
|
||||
|
||||
pub(super) fn fail_scalar(scalar: ScalarWithLambda) -> Instruction<'_> {
|
||||
Instruction::Fail(Fail::Scalar(scalar))
|
||||
}
|
||||
|
||||
pub(super) fn fail_literals(ret_code: i64, error_message: &str) -> Instruction<'_> {
|
||||
Instruction::Fail(Fail::Literal {
|
||||
ret_code,
|
||||
|
@ -16,6 +16,10 @@
|
||||
|
||||
use super::dsl::*;
|
||||
use super::parse;
|
||||
use crate::ast::ScalarWithLambda;
|
||||
|
||||
use air_lambda_ast::LambdaAST;
|
||||
use air_lambda_ast::ValueAccessor;
|
||||
|
||||
#[test]
|
||||
fn parse_fail_last_error() {
|
||||
@ -36,3 +40,31 @@ fn parse_fail_literals() {
|
||||
let expected = fail_literals(1, "error message");
|
||||
assert_eq!(instruction, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_fail_scalars() {
|
||||
let source_code = r#"
|
||||
(fail scalar)
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = fail_scalar(ScalarWithLambda::new("scalar", None, 18));
|
||||
assert_eq!(instruction, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_fail_scalar_with_lambda() {
|
||||
let source_code = r#"
|
||||
(fail scalar.$.field_accessor)
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = fail_scalar(ScalarWithLambda::new(
|
||||
"scalar",
|
||||
Some(unsafe {
|
||||
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName {
|
||||
field_name: "field_accessor",
|
||||
}])
|
||||
}),
|
||||
18,
|
||||
));
|
||||
assert_eq!(instruction, expected)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user