/* * 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::*; use crate::execution_step::value_types::populate_tetraplet_with_lambda; use crate::execution_step::value_types::IterableValue; use crate::execution_step::CatchableError; use crate::execution_step::PEEK_ALLOWED_ON_NON_EMPTY; use crate::JValue; use crate::LambdaAST; use crate::SecurityTetraplet; use air_interpreter_data::Provenance; use air_parser::ast; use std::ops::Deref; use std::rc::Rc; // TODO: refactor this file after switching to boxed value pub(crate) enum FoldIterableScalar { Empty, ScalarBased(IterableValue), } /// Creates iterable value for given scalar iterable. pub(crate) fn create_scalar_iterable( exec_ctx: &ExecutionCtx<'_>, variable_name: &str, ) -> ExecutionResult { match exec_ctx.scalars.get_value(variable_name)? { ScalarRef::Value(call_result) => from_value(call_result.clone(), variable_name), ScalarRef::IterableValue(fold_state) => { let iterable_value = fold_state.iterable.peek().expect(PEEK_ALLOWED_ON_NON_EMPTY); let call_result = iterable_value.into_resolved_result(); from_value(call_result, variable_name) } } } /// Creates iterable value for given scalar with lambda iterable. pub(crate) fn create_scalar_wl_iterable<'ctx>( scalar_iterable: &ast::ScalarWithLambda<'ctx>, exec_ctx: &ExecutionCtx<'ctx>, ) -> ExecutionResult { use crate::execution_step::lambda_applier::select_by_lambda_from_scalar; let scalar_name = scalar_iterable.name; let lambda = &scalar_iterable.lambda; match exec_ctx.scalars.get_value(scalar_name)? { ScalarRef::Value(variable) => { let jvalues = select_by_lambda_from_scalar(variable.get_result(), lambda, exec_ctx)?; let tetraplet = variable.get_tetraplet().deref().clone(); from_jvalue(&jvalues, tetraplet, variable.get_provenance(), lambda) } ScalarRef::IterableValue(fold_state) => { let iterable_value = fold_state.iterable.peek().unwrap(); let jvalue = iterable_value.apply_lambda(lambda, exec_ctx)?; let tetraplet = to_tetraplet(&iterable_value); let provenance = to_provenance(&iterable_value); from_jvalue(&jvalue, tetraplet, provenance, lambda) } } } /// Creates iterable value for given canon stream. pub(crate) fn create_canon_stream_iterable_value<'ctx>( ast_canon_stream: &ast::CanonStream<'ctx>, exec_ctx: &ExecutionCtx<'ctx>, ) -> ExecutionResult { let canon_stream = exec_ctx.scalars.get_canon_stream(ast_canon_stream.name)?; if canon_stream.is_empty() { return Ok(FoldIterableScalar::Empty); } // TODO: this one is a relatively long operation and will be refactored in Boxed Value let iterable_ingredients = CanonStreamIterableIngredients::init((**canon_stream).clone()); let iterable = Box::new(iterable_ingredients); Ok(FoldIterableScalar::ScalarBased(iterable)) } /// Creates iterable value for a canon stream map. pub(crate) fn create_canon_stream_map_iterable_value( ast_canon_stream_map: &ast::CanonStreamMap<'_>, exec_ctx: &ExecutionCtx<'_>, ) -> ExecutionResult { let canon_stream_map = exec_ctx.scalars.get_canon_map(ast_canon_stream_map.name)?; if canon_stream_map.is_empty() { return Ok(FoldIterableScalar::Empty); } // TODO: this one is a relatively long operation and will be refactored in Boxed Value // Can not create iterable from existing CanonStreamMap b/c CSM contains a map with // a limited lifetime but the boxed value needs static lifetime. let values = canon_stream_map.iter().cloned().collect::>(); let iterable_ingredients = CanonStreamMapIterableIngredients::init(values); let iterable = Box::new(iterable_ingredients); Ok(FoldIterableScalar::ScalarBased(iterable)) } /// Creates iterable value for a canon stream map with a lens applied. pub(crate) fn create_canon_stream_map_wl_iterable_value( ast_canon_stream_map: &ast::CanonStreamMapWithLambda<'_>, exec_ctx: &ExecutionCtx<'_>, ) -> ExecutionResult { let canon_stream_map = exec_ctx.scalars.get_canon_map(ast_canon_stream_map.name)?; // Source canon map provenance is used here to mimic the semantics used for scalar. let provenance = Provenance::canon(canon_stream_map.cid.clone()); if canon_stream_map.is_empty() { return Ok(FoldIterableScalar::Empty); } let canon_stream_map = &canon_stream_map.canon_stream_map; let tetraplet = canon_stream_map.tetraplet().deref().clone(); let lambda = &ast_canon_stream_map.lambda; // Source canon map tetraplet is used here similar with a scalar with lens processing path. let jvalues = JValuable::apply_lambda(&canon_stream_map, lambda, exec_ctx)?; from_jvalue(&jvalues, tetraplet, provenance, lambda) } /// Constructs iterable value from resolved call result. fn from_value(call_result: ValueAggregate, variable_name: &str) -> ExecutionResult { let len = match call_result.get_result() { JValue::Array(array) => { if array.is_empty() { // skip fold if array is empty return Ok(FoldIterableScalar::Empty); } array.len() } v => { return Err(CatchableError::FoldIteratesOverNonArray((*v).clone(), variable_name.to_string()).into()); } }; let foldable = IterableResolvedCall::init(call_result, len); let foldable = Box::new(foldable); let iterable = FoldIterableScalar::ScalarBased(foldable); Ok(iterable) } /// Construct IterableValue from the result and given triplet. fn from_jvalue( jvalue: &JValue, tetraplet: SecurityTetraplet, provenance: Provenance, lambda: &LambdaAST<'_>, ) -> ExecutionResult { let tetraplet = populate_tetraplet_with_lambda(tetraplet, lambda); let tetraplet = Rc::new(tetraplet); let iterable = match jvalue { JValue::Array(array) => array, _ => { return Err(CatchableError::FoldIteratesOverNonArray(jvalue.clone(), lambda.to_string()).into()); } }; if iterable.is_empty() { return Ok(FoldIterableScalar::Empty); } let iterable = iterable.to_vec(); let foldable = IterableLambdaResult::init(iterable, tetraplet, provenance); let iterable = FoldIterableScalar::ScalarBased(Box::new(foldable)); Ok(iterable) } fn to_tetraplet(iterable: &IterableItem<'_>) -> SecurityTetraplet { use IterableItem::*; let tetraplet = match iterable { RefValue((_, tetraplet, _, _)) => tetraplet, RcValue((_, tetraplet, _, _)) => tetraplet, }; (*tetraplet).deref().clone() } fn to_provenance(iterable: &IterableItem<'_>) -> Provenance { use IterableItem::*; let provenance = match iterable { RefValue((_, _, _, provenance)) => provenance, RcValue((_, _, _, provenance)) => provenance, }; provenance.clone() }