Add variables names in resolve errors (#194)

This commit is contained in:
Mike Voronov 2021-12-15 17:39:26 +03:00 committed by GitHub
parent 1d8182d497
commit 5ef8bfc940
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 66 additions and 54 deletions

View File

@ -20,13 +20,12 @@ use super::ExecutionResult;
use crate::exec_err; use crate::exec_err;
use crate::JValue; use crate::JValue;
use air_parser::ast::CallInstrValue; use air_parser::ast;
use air_parser::ast::Triplet;
use polyplets::ResolvedTriplet; use polyplets::ResolvedTriplet;
/// Resolve variables, literals, etc in the `Triplet`, and build a `ResolvedTriplet`. /// Resolve variables, literals, etc in the `Triplet`, and build a `ResolvedTriplet`.
pub(crate) fn resolve<'i>(triplet: &Triplet<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<ResolvedTriplet> { pub(crate) fn resolve<'i>(triplet: &ast::Triplet<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<ResolvedTriplet> {
let Triplet { let ast::Triplet {
peer_pk, peer_pk,
service_id, service_id,
function_name, function_name,
@ -45,26 +44,29 @@ pub(crate) fn resolve<'i>(triplet: &Triplet<'i>, ctx: &ExecutionCtx<'i>) -> Exec
/// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc. /// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc.
// TODO: return Rc<String> to avoid excess cloning // TODO: return Rc<String> to avoid excess cloning
fn resolve_to_string<'i>(value: &CallInstrValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<String> { fn resolve_to_string<'i>(value: &ast::CallInstrValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<String> {
use crate::execution_step::utils::resolve_ast_variable_wl; use crate::execution_step::utils::resolve_ast_variable_wl;
use ast::CallInstrValue::*;
let resolved = match value { let resolved = match value {
CallInstrValue::InitPeerId => ctx.init_peer_id.clone(), InitPeerId => ctx.init_peer_id.clone(),
CallInstrValue::Literal(value) => value.to_string(), Literal(value) => value.to_string(),
CallInstrValue::Variable(variable) => { Variable(variable) => {
let (resolved, _) = resolve_ast_variable_wl(variable, ctx)?; let (resolved, _) = resolve_ast_variable_wl(variable, ctx)?;
jvalue_to_string(resolved)? try_jvalue_to_string(resolved, variable)?
} }
}; };
Ok(resolved) Ok(resolved)
} }
fn jvalue_to_string(jvalue: JValue) -> ExecutionResult<String> { fn try_jvalue_to_string(jvalue: JValue, variable: &ast::VariableWithLambda<'_>) -> ExecutionResult<String> {
use ExecutionError::IncompatibleJValueType;
match jvalue { match jvalue {
JValue::String(s) => Ok(s), JValue::String(s) => Ok(s),
_ => exec_err!(IncompatibleJValueType(jvalue, "string")), _ => exec_err!(ExecutionError::IncompatibleJValueType {
variable_name: variable.name().to_string(),
actual_value: jvalue,
expected_value_type: "string",
}),
} }
} }

View File

@ -1,33 +0,0 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use super::ExecutionCtx;
use super::ExecutionResult;
use super::ExecutionError;
use super::TraceHandler;
use crate::log_instruction;
use air_parser::ast::Null;
impl<'i> super::ExecutableInstruction<'i> for Fail {
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
log_instruction!(null, exec_ctx, trace_ctx);
Err(ExecutionError::FailMet {
message: self.message.to_string()
})
}
}

View File

@ -87,19 +87,17 @@ fn create_scalar_iterable<'ctx>(
variable_name: &str, variable_name: &str,
) -> ExecutionResult<FoldIterableScalar> { ) -> ExecutionResult<FoldIterableScalar> {
match exec_ctx.scalars.get(variable_name)? { match exec_ctx.scalars.get(variable_name)? {
ScalarRef::Value(call_result) => from_call_result(call_result.clone()), ScalarRef::Value(call_result) => from_call_result(call_result.clone(), variable_name),
ScalarRef::IterableValue(fold_state) => { ScalarRef::IterableValue(fold_state) => {
let iterable_value = fold_state.iterable.peek().unwrap(); let iterable_value = fold_state.iterable.peek().unwrap();
let call_result = iterable_value.into_resolved_result(); let call_result = iterable_value.into_resolved_result();
from_call_result(call_result) from_call_result(call_result, variable_name)
} }
} }
} }
/// Constructs iterable value from resolved call result. /// Constructs iterable value from resolved call result.
fn from_call_result(call_result: ValueAggregate) -> ExecutionResult<FoldIterableScalar> { fn from_call_result(call_result: ValueAggregate, variable_name: &str) -> ExecutionResult<FoldIterableScalar> {
use ExecutionError::IncompatibleJValueType;
let len = match &call_result.result.deref() { let len = match &call_result.result.deref() {
JValue::Array(array) => { JValue::Array(array) => {
if array.is_empty() { if array.is_empty() {
@ -108,7 +106,13 @@ fn from_call_result(call_result: ValueAggregate) -> ExecutionResult<FoldIterable
} }
array.len() array.len()
} }
v => return exec_err!(IncompatibleJValueType((*v).clone(), "array")), v => {
return exec_err!(ExecutionError::IncompatibleJValueType {
variable_name: variable_name.to_string(),
actual_value: (*v).clone(),
expected_value_type: "array",
})
}
}; };
let foldable = IterableResolvedCall::init(call_result, len); let foldable = IterableResolvedCall::init(call_result, len);

View File

@ -59,8 +59,14 @@ pub(crate) enum ExecutionError {
EmptyStreamLambdaError, EmptyStreamLambdaError,
/// Provided JValue has incompatible type with a requested one. /// Provided JValue has incompatible type with a requested one.
#[error("expected JValue type '{1}', but got '{0}' JValue")] #[error(
IncompatibleJValueType(JValue, &'static str), "expected JValue type '{expected_value_type}' for the variable `{variable_name}`, but got '{actual_value}'"
)]
IncompatibleJValueType {
variable_name: String,
actual_value: JValue,
expected_value_type: &'static str,
},
/// Fold state wasn't found for such iterator name. /// Fold state wasn't found for such iterator name.
#[error("fold state not found for this iterable '{0}'")] #[error("fold state not found for this iterable '{0}'")]

View File

@ -18,6 +18,9 @@ use air::LastError;
use air::SecurityTetraplet; use air::SecurityTetraplet;
use air_test_utils::prelude::*; use air_test_utils::prelude::*;
use fstrings::f;
use fstrings::format_args_f;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -193,3 +196,33 @@ fn track_current_peer_id() {
let actual_value = (*args.borrow()).as_ref().unwrap().clone(); let actual_value = (*args.borrow()).as_ref().unwrap().clone();
assert_eq!(actual_value.peer_id, fallible_peer_id); assert_eq!(actual_value.peer_id, fallible_peer_id);
} }
#[test]
fn variable_names_shown_in_error() {
let set_variable_vm_peer_id = "set_variable_vm_peer_id";
let mut set_variable_vm = create_avm(set_variable_call_service(json!(1u32)), set_variable_vm_peer_id);
let echo_vm_peer_id = "echo_vm_peer_id";
let mut echo_vm = create_avm(echo_call_service(), echo_vm_peer_id);
let script = f!(r#"
(xor
(seq
(call "{set_variable_vm_peer_id}" ("" "") [""] -relay-)
(call -relay- ("" "") [])
)
(call "{echo_vm_peer_id}" ("" "") [%last_error%.$.msg])
)
"#);
let result = checked_call_vm!(set_variable_vm, "", &script, "", "");
let result = checked_call_vm!(echo_vm, "", script, "", result.data);
let trace = trace_from_result(&result);
assert_eq!(
trace[1],
executed_state::scalar(json!(
"expected JValue type 'string' for variable `-relay-`, but got '1'"
))
);
}