feat(parser,execution-engine): allow :error: in fail (#696)

The `(fail :error:)` was previously rejected by parser.  Now it is
executed, but is of limited use.
This commit is contained in:
Ivan Boldyrev 2023-09-19 11:21:30 +04:00 committed by GitHub
parent d41f7646d9
commit bd80a127ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1507 additions and 1307 deletions

View File

@ -46,6 +46,7 @@ impl<'i> super::ExecutableInstruction<'i> for Fail<'i> {
Fail::CanonStreamWithLambda(canon_stream) => fail_with_canon_stream(canon_stream, exec_ctx),
// bubble last error up
Fail::LastError => fail_with_last_error(exec_ctx),
Fail::Error => fail_with_error(exec_ctx),
}
}
}
@ -117,6 +118,18 @@ fn fail_with_last_error(exec_ctx: &mut ExecutionCtx<'_>) -> ExecutionResult<()>
fail_with_error_object(exec_ctx, error, tetraplet, provenance.clone())
}
fn fail_with_error(exec_ctx: &mut ExecutionCtx<'_>) -> ExecutionResult<()> {
use crate::execution_step::InstructionError;
let InstructionError {
error,
tetraplet,
provenance,
} = exec_ctx.error_descriptor.error();
fail_with_error_object(exec_ctx, error.clone(), tetraplet.clone(), provenance.clone())
}
fn fail_with_error_object(
exec_ctx: &mut ExecutionCtx<'_>,
error: Rc<JValue>,

View File

@ -44,6 +44,33 @@ fn fail_with_last_error() {
assert!(check_error(&result, expected_error));
}
#[test]
fn fail_with_error() {
let local_peer_id = "local_peer_id";
let fallible_service_id = "service_id_1";
let mut vm = create_avm(fallible_call_service(fallible_service_id), local_peer_id);
let script = format!(
r#"
(xor
(call "{local_peer_id}" ("service_id_1" "local_fn_name") [] result_1)
(fail :error:)
)"#
);
let result = call_vm!(vm, <_>::default(), script, "", "");
let expected_error = CatchableError::UserError {
error: rc!(json!({
"error_code": 10000i64,
"instruction": r#"call "local_peer_id" ("service_id_1" "local_fn_name") [] result_1"#,
"message": r#"Local service error, ret_code is 1, error message is '"failed result from fallible_call_service"'"#,
"peer_id": "local_peer_id",
})),
};
assert!(check_error(&result, expected_error));
}
#[test]
fn fail_with_literals() {
let local_peer_id = "local_peer_id";
@ -97,6 +124,34 @@ fn fail_with_last_error_tetraplets() {
);
}
#[test]
fn fail_with_error_tetraplets() {
let local_peer_id = "local_peer_id";
let fallible_service_id = "service_id_1";
let (host_closure, tetraplet_anchor) = tetraplet_host_function(fallible_call_service(fallible_service_id));
let mut vm = create_avm(host_closure, local_peer_id);
let local_fn_name = "local_fn_name";
let script = format!(
r#"
(xor
(xor
(call "{local_peer_id}" ("{fallible_service_id}" "{local_fn_name}") [] result_1)
(fail :error:)
)
(call "{local_peer_id}" ("" "") [%last_error%])
)
"#
);
let test_params = TestRunParameters::from_init_peer_id(local_peer_id);
let _ = checked_call_vm!(vm, test_params, script, "", "");
assert_eq!(
tetraplet_anchor.borrow()[0][0],
SecurityTetraplet::new(local_peer_id, fallible_service_id, local_fn_name, "")
);
}
#[test]
fn fail_with_literals_tetraplets() {
let local_peer_id = "local_peer_id";

View File

@ -137,6 +137,7 @@ pub enum Fail<'i> {
},
CanonStreamWithLambda(CanonStreamWithLambda<'i>),
LastError,
Error,
}
/// (fold scalar_iterable iterator instruction)

View File

@ -111,6 +111,7 @@ impl fmt::Display for Fail<'_> {
write!(f, "fail {stream}")
}
Fail::LastError => write!(f, "fail %last_error%"),
Fail::Error => write!(f, "fail :error:"),
}
}
}

View File

@ -190,6 +190,9 @@ FailBody: Fail<'input> = {
<canon_stream:CanonStreamWithLambda> => Fail::CanonStreamWithLambda(CanonStreamWithLambda::new(canon_stream.0, canon_stream.1, canon_stream.2)),
<left: @L> <l:LastError> <right: @R> => {
Fail::LastError
},
<left: @L> <l:Error> <right: @R> => {
Fail::Error
}
}

File diff suppressed because it is too large Load Diff

View File

@ -381,3 +381,10 @@ fn deeply_nested() {
let expected = include_str!("deeply_nested_expected.txt");
assert_eq!(output, expected);
}
#[test]
fn fail_error() {
let script = r#"(fail :error:)"#;
let output = beautify_to_string(script).unwrap();
assert_eq!(output, "fail :error:\n");
}