diff --git a/eval/compiler/BUILD b/eval/compiler/BUILD index 032a12c74..541eb4d79 100644 --- a/eval/compiler/BUILD +++ b/eval/compiler/BUILD @@ -36,6 +36,7 @@ cc_library( "//common:value", "//eval/eval:direct_expression_step", "//eval/eval:evaluator_core", + "//eval/eval:expression_step_logic", "//eval/eval:trace_step", "//internal:casts", "//runtime:runtime_options", @@ -111,7 +112,6 @@ cc_library( "//common:type", "//common:type_spec_resolver", "//common:value", - "//eval/eval:comprehension_step", "//eval/eval:const_value_step", "//eval/eval:container_access_step", "//eval/eval:create_list_step", @@ -120,6 +120,7 @@ cc_library( "//eval/eval:direct_expression_step", "//eval/eval:equality_steps", "//eval/eval:evaluator_core", + "//eval/eval:expression_step_logic", "//eval/eval:function_step", "//eval/eval:ident_step", "//eval/eval:jump_step", diff --git a/eval/compiler/flat_expr_builder.cc b/eval/compiler/flat_expr_builder.cc index 6c82eb912..514e28df4 100644 --- a/eval/compiler/flat_expr_builder.cc +++ b/eval/compiler/flat_expr_builder.cc @@ -31,6 +31,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/base/nullability.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/container/node_hash_map.h" @@ -71,6 +72,7 @@ #include "eval/eval/direct_expression_step.h" #include "eval/eval/equality_steps.h" #include "eval/eval/evaluator_core.h" +#include "eval/eval/expression_step_logic.h" #include "eval/eval/function_step.h" #include "eval/eval/ident_step.h" #include "eval/eval/jump_step.h" @@ -427,13 +429,11 @@ bool IsBlock(const cel::CallExpr* call) { return call->function() == kBlock; } // Visitor for Comprehension expressions. class ComprehensionVisitor { public: - explicit ComprehensionVisitor(FlatExprVisitor* visitor, bool short_circuiting, - bool is_trivial, size_t iter_slot, - size_t iter2_slot, size_t accu_slot) + explicit ComprehensionVisitor(FlatExprVisitor* visitor, bool is_trivial, + size_t iter_slot, size_t iter2_slot, + size_t accu_slot) : visitor_(visitor), - next_step_(nullptr), - cond_step_(nullptr), - short_circuiting_(short_circuiting), + init_step_(nullptr), is_trivial_(is_trivial), accu_init_extracted_(false), iter_slot_(iter_slot), @@ -461,14 +461,14 @@ class ComprehensionVisitor { absl::Status PostVisitArgDefault(cel::ComprehensionArg arg_num, const cel::Expr* comprehension_expr); + ComprehensionCondStep* absl_nullable GetCondStep(); + ComprehensionNextStep* absl_nullable GetNextStep(); + FlatExprVisitor* visitor_; ComprehensionInitStep* init_step_; - ComprehensionNextStep* next_step_; - ComprehensionCondStep* cond_step_; ProgramStepIndex init_step_pos_; - ProgramStepIndex next_step_pos_; - ProgramStepIndex cond_step_pos_; - bool short_circuiting_; + std::optional next_step_pos_; + std::optional cond_step_pos_; bool is_trivial_; bool accu_init_extracted_; size_t iter_slot_; @@ -865,8 +865,7 @@ class FlatExprVisitor : public cel::AstVisitor { CreateDirectSlotIdentStep(ident_expr.name(), slot.slot, expr.id()), 1); } else { - AddStep(CreateIdentStepForSlot(ident_expr.name(), slot.slot), - expr.id()); + AddStep(ExpressionStep::MakeReadSlotStep(slot.slot, expr.id())); } return; } @@ -1439,9 +1438,8 @@ class FlatExprVisitor : public cel::AstVisitor { /*.iter_var2_in_scope=*/false, /*.accu_var_in_scope=*/false, /*.in_accu_init=*/false, - std::make_unique(this, options_.short_circuiting, - is_bind, iter_slot, iter2_slot, - accu_slot)}); + std::make_unique(this, is_bind, iter_slot, + iter2_slot, accu_slot)}); comprehension_stack_.back().visitor->PreVisit(&expr); } @@ -2433,6 +2431,30 @@ void ComprehensionVisitor::PreVisit(const cel::Expr* expr) { } } +ComprehensionCondStep* absl_nullable ComprehensionVisitor::GetCondStep() { + if (!cond_step_pos_) { + return nullptr; + } + ExpressionStep* step = + cond_step_pos_->subexpression->GetIfExpressionStep(cond_step_pos_->index); + if (!step) { + return nullptr; + } + return GetIfComprehensionCondStep(*step); +} + +ComprehensionNextStep* absl_nullable ComprehensionVisitor::GetNextStep() { + if (!next_step_pos_) { + return nullptr; + } + ExpressionStep* step = + next_step_pos_->subexpression->GetIfExpressionStep(next_step_pos_->index); + if (!step) { + return nullptr; + } + return GetIfComprehensionNextStep(*step); +} + absl::Status ComprehensionVisitor::PostVisitArgDefault( cel::ComprehensionArg arg_num, const cel::Expr* expr) { if (visitor_->PlanRecursiveProgram()) { @@ -2441,19 +2463,31 @@ absl::Status ComprehensionVisitor::PostVisitArgDefault( switch (arg_num) { case cel::ITER_RANGE: { init_step_pos_ = visitor_->GetCurrentIndex(); - init_step_ = visitor_->AddStep(std::make_unique()); + if (iter_slot_ != iter2_slot_) { + init_step_ = visitor_->AddStep(std::make_unique( + iter_slot_, iter2_slot_, accu_slot_)); + } else { + init_step_ = visitor_->AddStep( + std::make_unique(iter_slot_, accu_slot_)); + } break; } case cel::ACCU_INIT: { next_step_pos_ = visitor_->GetCurrentIndex(); - next_step_ = visitor_->AddStep(std::make_unique( - iter_slot_, iter2_slot_, accu_slot_)); + if (iter_slot_ != iter2_slot_) { + visitor_->AddStep(ExpressionStep::MakeComprehensionNext2Step()); + } else { + visitor_->AddStep(ExpressionStep::MakeComprehensionNextStep()); + } break; } case cel::LOOP_CONDITION: { cond_step_pos_ = visitor_->GetCurrentIndex(); - cond_step_ = visitor_->AddStep(std::make_unique( - iter_slot_, iter2_slot_, accu_slot_, short_circuiting_)); + if (iter_slot_ != iter2_slot_) { + visitor_->AddStep(ExpressionStep::MakeComprehensionCond2Step()); + } else { + visitor_->AddStep(ExpressionStep::MakeComprehensionCondStep()); + } break; } case cel::LOOP_STEP: { @@ -2464,46 +2498,51 @@ absl::Status ComprehensionVisitor::PostVisitArgDefault( } Jump jump_helper(index, jump_to_next); visitor_->SetProgressStatusIfError( - jump_helper.set_target(next_step_pos_)); + jump_helper.set_target(*next_step_pos_)); // Set offsets jumping to the result step. - if (cond_step_) { - CEL_ASSIGN_OR_RETURN( - int jump_from_cond, - Jump::CalculateOffset(cond_step_pos_, visitor_->GetCurrentIndex())); - cond_step_->set_jump_offset(jump_from_cond); + if (auto* cond_step = GetCondStep(); cond_step != nullptr) { + CEL_ASSIGN_OR_RETURN(int jump_from_cond, + Jump::CalculateOffset( + *cond_step_pos_, visitor_->GetCurrentIndex())); + cond_step->set_jump_offset(jump_from_cond); } - if (next_step_) { - CEL_ASSIGN_OR_RETURN( - int jump_from_next, - Jump::CalculateOffset(next_step_pos_, visitor_->GetCurrentIndex())); + if (auto* next_step = GetNextStep(); next_step != nullptr) { + CEL_ASSIGN_OR_RETURN(int jump_from_next, + Jump::CalculateOffset( + *next_step_pos_, visitor_->GetCurrentIndex())); - next_step_->set_jump_offset(jump_from_next); + next_step->set_jump_offset(jump_from_next); } break; } case cel::RESULT: { - if (!init_step_ || !next_step_ || !cond_step_) { + if (!init_step_ || !next_step_pos_ || !cond_step_pos_) { // Encountered an error earlier. Can't determine where to jump. break; } - visitor_->AddStep(CreateComprehensionFinishStep(accu_slot_), expr->id()); + visitor_->AddStep( + ExpressionStep::MakeComprehensionFinishStep(accu_slot_, expr->id())); // Set offsets jumping past the result step in case of errors. CEL_ASSIGN_OR_RETURN( int jump_from_init, Jump::CalculateOffset(init_step_pos_, visitor_->GetCurrentIndex())); init_step_->set_error_jump_offset(jump_from_init); - CEL_ASSIGN_OR_RETURN( - int jump_from_next, - Jump::CalculateOffset(next_step_pos_, visitor_->GetCurrentIndex())); - next_step_->set_error_jump_offset(jump_from_next); + if (auto* next_step = GetNextStep(); next_step != nullptr) { + CEL_ASSIGN_OR_RETURN(int jump_from_next, + Jump::CalculateOffset( + *next_step_pos_, visitor_->GetCurrentIndex())); + next_step->set_error_jump_offset(jump_from_next); + } - CEL_ASSIGN_OR_RETURN( - int jump_from_cond, - Jump::CalculateOffset(cond_step_pos_, visitor_->GetCurrentIndex())); - cond_step_->set_error_jump_offset(jump_from_cond); + if (auto* cond_step = GetCondStep(); cond_step != nullptr) { + CEL_ASSIGN_OR_RETURN(int jump_from_cond, + Jump::CalculateOffset( + *cond_step_pos_, visitor_->GetCurrentIndex())); + cond_step->set_error_jump_offset(jump_from_cond); + } break; } } diff --git a/eval/compiler/flat_expr_builder_extensions.h b/eval/compiler/flat_expr_builder_extensions.h index 2ce70a166..e85f337f9 100644 --- a/eval/compiler/flat_expr_builder_extensions.h +++ b/eval/compiler/flat_expr_builder_extensions.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "absl/base/attributes.h" @@ -45,6 +46,7 @@ #include "eval/compiler/resolver.h" #include "eval/eval/direct_expression_step.h" #include "eval/eval/evaluator_core.h" +#include "eval/eval/expression_step_logic.h" #include "eval/eval/trace_step.h" #include "internal/casts.h" #include "runtime/internal/issue_collector.h" @@ -125,6 +127,13 @@ class ProgramBuilder { elements().push_back(expr); } + ExpressionStep* absl_nullable GetIfExpressionStep(size_t index) { + if (index >= elements().size()) { + return nullptr; + } + return std::get_if(&elements()[index]); + } + // Accessor for elements (either simple steps or subexpressions). // // Value is undefined if in the expression has already been flattened. @@ -288,9 +297,8 @@ class ProgramBuilder { // Add a program step to the current subexpression. // If successful, returns the step pointer. // - // Note: If successful, the pointer should remain valid until the parent - // expression is finalized. Optimizers may modify the program plan which may - // free the step at that point. + // Note: If successful, the pointer should remain valid until a further call + // to AddStep, AddSubexpression, or Flatten. ExpressionStep* absl_nullable AddStep(ExpressionStep step); void Reset(); diff --git a/eval/eval/BUILD b/eval/eval/BUILD index 8a1cc795e..5dfaf69a1 100644 --- a/eval/eval/BUILD +++ b/eval/eval/BUILD @@ -35,11 +35,13 @@ package_group( cc_library( name = "evaluator_core", srcs = [ + "comprehension_step.cc", "evaluator_core.cc", "lazy_init_step.cc", "logic_step.cc", ], hdrs = [ + "comprehension_step.h", "evaluator_core.h", "lazy_init_step.h", "logic_step.h", @@ -50,7 +52,9 @@ cc_library( ":comprehension_slots", ":direct_expression_step", ":evaluator_stack", + ":expression_step_logic", ":iterator_stack", + "//base:attributes", "//base:builtins", "//base:data", "//common:casting", @@ -64,6 +68,7 @@ cc_library( "//runtime:runtime_options", "//runtime/internal:activation_attribute_matcher_access", "//runtime/internal:errors", + "@com_google_absl//absl/base", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/log:absl_check", @@ -180,12 +185,26 @@ cc_test( ], ) +cc_library( + name = "expression_step_logic", + hdrs = [ + "expression_step_logic.h", + ], + deps = [ + "//common:native_type", + "@com_google_absl//absl/status", + ], +) + cc_library( name = "expression_step_base", hdrs = [ "expression_step_base.h", ], - deps = [":evaluator_core"], + deps = [ + ":evaluator_core", + ":expression_step_logic", + ], ) cc_library( @@ -267,6 +286,7 @@ cc_library( ":direct_expression_step", ":evaluator_core", ":expression_step_base", + ":expression_step_logic", "//common:value", "//eval/internal:errors", "//internal:status_macros", @@ -490,35 +510,6 @@ cc_test( ], ) -cc_library( - name = "comprehension_step", - srcs = [ - "comprehension_step.cc", - ], - hdrs = [ - "comprehension_step.h", - ], - deps = [ - ":attribute_trail", - ":comprehension_slots", - ":direct_expression_step", - ":evaluator_core", - ":expression_step_base", - "//base:attributes", - "//common:casting", - "//common:value", - "//common:value_kind", - "//eval/internal:errors", - "//internal:status_macros", - "@com_google_absl//absl/base", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/base:nullability", - "@com_google_absl//absl/log:absl_check", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - ], -) - cc_test( name = "comprehension_step_test", size = "small", @@ -529,7 +520,6 @@ cc_test( ":attribute_trail", ":cel_expression_flat_impl", ":comprehension_slots", - ":comprehension_step", ":const_value_step", ":direct_expression_step", ":evaluator_core", diff --git a/eval/eval/comprehension_step.cc b/eval/eval/comprehension_step.cc index 7f6d5b4e2..d67a7e8a6 100644 --- a/eval/eval/comprehension_step.cc +++ b/eval/eval/comprehension_step.cc @@ -20,7 +20,7 @@ #include "eval/eval/comprehension_slots.h" #include "eval/eval/direct_expression_step.h" #include "eval/eval/evaluator_core.h" -#include "eval/eval/expression_step_base.h" +#include "eval/eval/iterator_stack.h" #include "eval/internal/errors.h" #include "internal/status_macros.h" @@ -41,6 +41,7 @@ using ::cel::ValueIterator; using ::cel::ValueIteratorPtr; using ::cel::ValueKind; using ::cel::runtime_internal::CreateNoMatchingOverloadError; +using ::cel::runtime_internal::IteratorStack; AttributeQualifier AttributeQualifierFromValue(const Value& v) { switch (v.kind()) { @@ -58,25 +59,6 @@ AttributeQualifier AttributeQualifierFromValue(const Value& v) { } } -class ComprehensionFinishStep final : public ExpressionStepBase { - public: - explicit ComprehensionFinishStep(size_t accu_slot) - : ExpressionStepBase(), accu_slot_(accu_slot) {} - - absl::Status Evaluate(ExecutionFrame* frame) const override { - if (!frame->value_stack().HasEnough(2)) { - return absl::Status(absl::StatusCode::kInternal, "Value stack underflow"); - } - frame->value_stack().SwapAndPop(2, 1); - frame->comprehension_slots().ClearSlot(accu_slot_); - frame->iterator_stack().Pop(); - return absl::OkStatus(); - } - - private: - const size_t accu_slot_; -}; - class ComprehensionDirectStep final : public DirectExpressionStep { public: explicit ComprehensionDirectStep( @@ -468,11 +450,23 @@ absl::Status ComprehensionInitStep::Evaluate(ExecutionFrame* frame) const { switch (top.kind()) { case ValueKind::kList: { CEL_ASSIGN_OR_RETURN(auto iterator, top.GetList().NewIterator()); - frame->iterator_stack().Push(std::move(iterator)); + if (has_iter2_) { + frame->iterator_stack().Push(std::move(iterator), iter_slot_, + iter2_slot_, accu_slot_); + } else { + frame->iterator_stack().Push(std::move(iterator), iter_slot_, + accu_slot_); + } } break; case ValueKind::kMap: { CEL_ASSIGN_OR_RETURN(auto iterator, top.GetMap().NewIterator()); - frame->iterator_stack().Push(std::move(iterator)); + if (has_iter2_) { + frame->iterator_stack().Push(std::move(iterator), iter_slot_, + iter2_slot_, accu_slot_); + } else { + frame->iterator_stack().Push(std::move(iterator), iter_slot_, + accu_slot_); + } } break; default: // Replace with an error and jump past @@ -485,21 +479,28 @@ absl::Status ComprehensionInitStep::Evaluate(ExecutionFrame* frame) const { return absl::OkStatus(); } -absl::Status ComprehensionNextStep::Evaluate1(ExecutionFrame* frame) const { +void ComprehensionNextStep::Evaluate1(ExecutionFrame* frame) const { + if (frame->iterator_stack().empty()) { + frame->Abort(absl::InternalError("Iterator stack underflow")); + return; + } + const IteratorStack::Entry& entry = *frame->iterator_stack().Peek(); if (!frame->value_stack().HasEnough(2)) { - return absl::Status(absl::StatusCode::kInternal, "Value stack underflow"); + frame->Abort( + absl::Status(absl::StatusCode::kInternal, "Value stack underflow")); + return; } { Value& accu_var = frame->value_stack().Peek(); AttributeTrail& accu_var_attr = frame->value_stack().PeekAttribute(); - frame->comprehension_slots().Set(accu_slot_, std::move(accu_var), + frame->comprehension_slots().Set(entry.accu_slot, std::move(accu_var), std::move(accu_var_attr)); frame->value_stack().Pop(1); } ComprehensionSlots::Slot* iter_slot = - frame->comprehension_slots().Get(iter_slot_); + frame->comprehension_slots().Get(entry.iter_slot); ABSL_DCHECK(iter_slot != nullptr); iter_slot->Set(); @@ -519,15 +520,23 @@ absl::Status ComprehensionNextStep::Evaluate1(ExecutionFrame* frame) const { default: ABSL_UNREACHABLE(); } - CEL_ASSIGN_OR_RETURN(bool ok, - frame->iterator_stack().Peek()->Next2( - frame->descriptor_pool(), frame->message_factory(), - frame->arena(), key, value)); - if (!ok) { + absl::StatusOr ok = entry.iterator->Next2(frame->descriptor_pool(), + frame->message_factory(), + frame->arena(), key, value); + if (!ok.ok()) { + frame->Abort(std::move(ok).status()); + return; + } + if (!*ok) { iter_slot->Clear(); - return frame->JumpTo(jump_offset_); + frame->JumpToOrAbort(jump_offset_); + return; + } + absl::Status inc_status = frame->IncrementIterations(); + if (!inc_status.ok()) { + frame->Abort(std::move(inc_status)); + return; } - CEL_RETURN_IF_ERROR(frame->IncrementIterations()); *iter_slot->mutable_attribute() = frame->value_stack().PeekAttribute().Step( AttributeQualifierFromValue(*key)); if (frame->attribute_utility().CheckForUnknownExact( @@ -536,53 +545,74 @@ absl::Status ComprehensionNextStep::Evaluate1(ExecutionFrame* frame) const { iter_slot->attribute().attribute()); } } else { - CEL_ASSIGN_OR_RETURN(bool ok, - frame->iterator_stack().Peek()->Next1( - frame->descriptor_pool(), frame->message_factory(), - frame->arena(), iter_slot->mutable_value())); - if (!ok) { + absl::StatusOr ok = entry.iterator->Next1( + frame->descriptor_pool(), frame->message_factory(), frame->arena(), + iter_slot->mutable_value()); + if (!ok.ok()) { + frame->Abort(std::move(ok).status()); + return; + } + if (!*ok) { iter_slot->Clear(); - return frame->JumpTo(jump_offset_); + frame->JumpToOrAbort(jump_offset_); + return; + } + absl::Status inc_status = frame->IncrementIterations(); + if (!inc_status.ok()) { + frame->Abort(std::move(inc_status)); + return; } - CEL_RETURN_IF_ERROR(frame->IncrementIterations()); } - return absl::OkStatus(); } -absl::Status ComprehensionNextStep::Evaluate2(ExecutionFrame* frame) const { +void ComprehensionNextStep::Evaluate2(ExecutionFrame* frame) const { + if (frame->iterator_stack().empty()) { + frame->Abort(absl::InternalError("Iterator stack underflow")); + return; + } + const IteratorStack::Entry& entry = *frame->iterator_stack().Peek(); if (!frame->value_stack().HasEnough(2)) { - return absl::Status(absl::StatusCode::kInternal, "Value stack underflow"); + frame->Abort( + absl::Status(absl::StatusCode::kInternal, "Value stack underflow")); + return; } { Value& accu_var = frame->value_stack().Peek(); AttributeTrail& accu_var_attr = frame->value_stack().PeekAttribute(); - frame->comprehension_slots().Set(accu_slot_, std::move(accu_var), + frame->comprehension_slots().Set(entry.accu_slot, std::move(accu_var), std::move(accu_var_attr)); frame->value_stack().Pop(1); } ComprehensionSlots::Slot* iter_slot = - frame->comprehension_slots().Get(iter_slot_); + frame->comprehension_slots().Get(entry.iter_slot); ABSL_DCHECK(iter_slot != nullptr); iter_slot->Set(); ComprehensionSlots::Slot* iter2_slot = - frame->comprehension_slots().Get(iter2_slot_); + frame->comprehension_slots().Get(entry.iter2_slot); ABSL_DCHECK(iter2_slot != nullptr); iter2_slot->Set(); - CEL_ASSIGN_OR_RETURN( - bool ok, - frame->iterator_stack().Peek()->Next2( - frame->descriptor_pool(), frame->message_factory(), frame->arena(), - iter_slot->mutable_value(), iter2_slot->mutable_value())); - if (!ok) { + absl::StatusOr ok = entry.iterator->Next2( + frame->descriptor_pool(), frame->message_factory(), frame->arena(), + iter_slot->mutable_value(), iter2_slot->mutable_value()); + if (!ok.ok()) { + frame->Abort(std::move(ok).status()); + return; + } + if (!*ok) { iter_slot->Clear(); iter2_slot->Clear(); - return frame->JumpTo(jump_offset_); + frame->JumpToOrAbort(jump_offset_); + return; + } + absl::Status inc_status = frame->IncrementIterations(); + if (!inc_status.ok()) { + frame->Abort(std::move(inc_status)); + return; } - CEL_RETURN_IF_ERROR(frame->IncrementIterations()); if (frame->enable_unknowns()) { *iter_slot->mutable_attribute() = *iter2_slot->mutable_attribute() = frame->value_stack().PeekAttribute().Step( @@ -594,12 +624,18 @@ absl::Status ComprehensionNextStep::Evaluate2(ExecutionFrame* frame) const { iter2_slot->attribute().attribute()); } } - return absl::OkStatus(); } -absl::Status ComprehensionCondStep::Evaluate1(ExecutionFrame* frame) const { +void ComprehensionCondStep::Evaluate1(ExecutionFrame* frame) const { + if (frame->iterator_stack().empty()) { + frame->Abort(absl::InternalError("Iterator stack underflow")); + return; + } + const IteratorStack::Entry& entry = *frame->iterator_stack().Peek(); if (!frame->value_stack().HasEnough(2)) { - return absl::Status(absl::StatusCode::kInternal, "Value stack underflow"); + frame->Abort( + absl::Status(absl::StatusCode::kInternal, "Value stack underflow")); + return; } const Value& top = frame->value_stack().Peek(); switch (top.kind()) { @@ -607,33 +643,45 @@ absl::Status ComprehensionCondStep::Evaluate1(ExecutionFrame* frame) const { break; case ValueKind::kError: ABSL_FALLTHROUGH_INTENDED; - case ValueKind::kUnknown: + case ValueKind::kUnknown: { frame->value_stack().SwapAndPop(2, 1); - frame->comprehension_slots().ClearSlot(iter_slot_); - frame->comprehension_slots().ClearSlot(accu_slot_); + frame->comprehension_slots().ClearSlot(entry.iter_slot); + frame->comprehension_slots().ClearSlot(entry.accu_slot); frame->iterator_stack().Pop(); - return frame->JumpTo(error_jump_offset_); - default: + frame->JumpToOrAbort(error_jump_offset_); + return; + } + default: { frame->value_stack().PopAndPush( 2, cel::ErrorValue::From( CreateNoMatchingOverloadError(""), frame->arena())); - frame->comprehension_slots().ClearSlot(iter_slot_); - frame->comprehension_slots().ClearSlot(accu_slot_); + frame->comprehension_slots().ClearSlot(entry.iter_slot); + frame->comprehension_slots().ClearSlot(entry.accu_slot); frame->iterator_stack().Pop(); - return frame->JumpTo(error_jump_offset_); + frame->JumpToOrAbort(error_jump_offset_); + return; + } } const bool loop_condition = absl::implicit_cast(top.GetBool()); + const bool short_circuiting = frame->options().short_circuiting; frame->value_stack().Pop(1); // loop_condition - if (!loop_condition && shortcircuiting_) { - return frame->JumpTo(jump_offset_); + if (!loop_condition && short_circuiting) { + frame->JumpToOrAbort(jump_offset_); + return; } - return absl::OkStatus(); } -absl::Status ComprehensionCondStep::Evaluate2(ExecutionFrame* frame) const { +void ComprehensionCondStep::Evaluate2(ExecutionFrame* frame) const { + if (frame->iterator_stack().empty()) { + frame->Abort(absl::InternalError("Iterator stack underflow")); + return; + } + const IteratorStack::Entry& entry = *frame->iterator_stack().Peek(); if (!frame->value_stack().HasEnough(2)) { - return absl::Status(absl::StatusCode::kInternal, "Value stack underflow"); + frame->Abort( + absl::Status(absl::StatusCode::kInternal, "Value stack underflow")); + return; } const Value& top = frame->value_stack().Peek(); switch (top.kind()) { @@ -641,30 +689,35 @@ absl::Status ComprehensionCondStep::Evaluate2(ExecutionFrame* frame) const { break; case ValueKind::kError: ABSL_FALLTHROUGH_INTENDED; - case ValueKind::kUnknown: + case ValueKind::kUnknown: { frame->value_stack().SwapAndPop(2, 1); - frame->comprehension_slots().ClearSlot(iter_slot_); - frame->comprehension_slots().ClearSlot(iter2_slot_); - frame->comprehension_slots().ClearSlot(accu_slot_); + frame->comprehension_slots().ClearSlot(entry.iter_slot); + frame->comprehension_slots().ClearSlot(entry.iter2_slot); + frame->comprehension_slots().ClearSlot(entry.accu_slot); frame->iterator_stack().Pop(); - return frame->JumpTo(error_jump_offset_); - default: + frame->JumpToOrAbort(error_jump_offset_); + return; + } + default: { frame->value_stack().PopAndPush( 2, cel::ErrorValue::From( CreateNoMatchingOverloadError(""), frame->arena())); - frame->comprehension_slots().ClearSlot(iter_slot_); - frame->comprehension_slots().ClearSlot(iter2_slot_); - frame->comprehension_slots().ClearSlot(accu_slot_); + frame->comprehension_slots().ClearSlot(entry.iter_slot); + frame->comprehension_slots().ClearSlot(entry.iter2_slot); + frame->comprehension_slots().ClearSlot(entry.accu_slot); frame->iterator_stack().Pop(); - return frame->JumpTo(error_jump_offset_); + frame->JumpToOrAbort(error_jump_offset_); + return; + } } const bool loop_condition = absl::implicit_cast(top.GetBool()); + const bool short_circuiting = frame->options().short_circuiting; frame->value_stack().Pop(1); // loop_condition - if (!loop_condition && shortcircuiting_) { - return frame->JumpTo(jump_offset_); + if (!loop_condition && short_circuiting) { + frame->JumpToOrAbort(jump_offset_); + return; } - return absl::OkStatus(); } std::unique_ptr CreateDirectComprehensionStep( @@ -681,9 +734,15 @@ std::unique_ptr CreateDirectComprehensionStep( shortcircuiting, expr_id); } -std::unique_ptr CreateComprehensionFinishStep( - size_t accu_slot) { - return std::make_unique(accu_slot); +void EvaluateComprehensionFinishStep(size_t accu_slot, ExecutionFrame& frame) { + if (!frame.value_stack().HasEnough(2)) { + frame.Abort( + absl::Status(absl::StatusCode::kInternal, "Value stack underflow")); + return; + } + frame.value_stack().SwapAndPop(2, 1); + frame.comprehension_slots().ClearSlot(accu_slot); + frame.iterator_stack().Pop(); } } // namespace google::api::expr::runtime diff --git a/eval/eval/comprehension_step.h b/eval/eval/comprehension_step.h index f16b62e13..8ec7960db 100644 --- a/eval/eval/comprehension_step.h +++ b/eval/eval/comprehension_step.h @@ -8,8 +8,8 @@ #include "absl/status/status.h" #include "eval/eval/direct_expression_step.h" -#include "eval/eval/evaluator_core.h" -#include "eval/eval/expression_step_base.h" +#include "eval/eval/expression_step_logic.h" +#include "eval/eval/iterator_stack.h" namespace google::api::expr::runtime { @@ -25,75 +25,63 @@ namespace google::api::expr::runtime { // 8: 1 -> 2 // 9: ComprehensionFinishStep 2 -> 1 -class ComprehensionInitStep final : public ExpressionStepBase { +class ComprehensionInitStep final : public ExpressionStepLogic { public: - ComprehensionInitStep() : ExpressionStepBase() {} + ComprehensionInitStep(size_t iter_slot, size_t iter2_slot, size_t accu_slot) + : iter_slot_(iter_slot), + iter2_slot_(iter2_slot), + accu_slot_(accu_slot), + has_iter2_(true) {} + ComprehensionInitStep(size_t iter_slot, size_t accu_slot) + : iter_slot_(iter_slot), + iter2_slot_(0), + accu_slot_(accu_slot), + has_iter2_(false) {} void set_error_jump_offset(int offset) { error_jump_offset_ = offset; } absl::Status Evaluate(ExecutionFrame* frame) const override; private: + const size_t iter_slot_; + const size_t iter2_slot_; + const size_t accu_slot_; + bool has_iter2_ = false; int error_jump_offset_ = std::numeric_limits::max(); }; -class ComprehensionNextStep final : public ExpressionStepBase { +class ComprehensionNextStep final { public: - ComprehensionNextStep(size_t iter_slot, size_t iter2_slot, size_t accu_slot) - : ExpressionStepBase(), - iter_slot_(iter_slot), - iter2_slot_(iter2_slot), - accu_slot_(accu_slot) {} + ComprehensionNextStep() = default; void set_jump_offset(int offset) { jump_offset_ = offset; } void set_error_jump_offset(int offset) { error_jump_offset_ = offset; } - absl::Status Evaluate(ExecutionFrame* frame) const override { - return iter_slot_ == iter2_slot_ ? Evaluate1(frame) : Evaluate2(frame); - } - - private: - absl::Status Evaluate1(ExecutionFrame* frame) const; + void Evaluate1(ExecutionFrame* frame) const; - absl::Status Evaluate2(ExecutionFrame* frame) const; + void Evaluate2(ExecutionFrame* frame) const; - const size_t iter_slot_; - const size_t iter2_slot_; - const size_t accu_slot_; - int jump_offset_ = std::numeric_limits::max(); - int error_jump_offset_ = std::numeric_limits::max(); + private: + int32_t jump_offset_ = std::numeric_limits::max(); + int32_t error_jump_offset_ = std::numeric_limits::max(); }; -class ComprehensionCondStep final : public ExpressionStepBase { +class ComprehensionCondStep final { public: - ComprehensionCondStep(size_t iter_slot, size_t iter2_slot, size_t accu_slot, - bool shortcircuiting) - : ExpressionStepBase(), - iter_slot_(iter_slot), - iter2_slot_(iter2_slot), - accu_slot_(accu_slot), - shortcircuiting_(shortcircuiting) {} + ComprehensionCondStep() = default; void set_jump_offset(int offset) { jump_offset_ = offset; } void set_error_jump_offset(int offset) { error_jump_offset_ = offset; } - absl::Status Evaluate(ExecutionFrame* frame) const override { - return iter_slot_ == iter2_slot_ ? Evaluate1(frame) : Evaluate2(frame); - } - - private: - absl::Status Evaluate1(ExecutionFrame* frame) const; + void Evaluate1(ExecutionFrame* frame) const; - absl::Status Evaluate2(ExecutionFrame* frame) const; + void Evaluate2(ExecutionFrame* frame) const; - const size_t iter_slot_; - const size_t iter2_slot_; - const size_t accu_slot_; - int jump_offset_ = std::numeric_limits::max(); - int error_jump_offset_ = std::numeric_limits::max(); - const bool shortcircuiting_; + private: + int32_t jump_offset_ = std::numeric_limits::max(); + int32_t error_jump_offset_ = std::numeric_limits::max(); }; // Creates a step for executing a comprehension. @@ -106,11 +94,10 @@ std::unique_ptr CreateDirectComprehensionStep( std::unique_ptr result_step, bool shortcircuiting, int64_t expr_id); -// Creates a cleanup step for the comprehension. +// Runs a cleanup step for the comprehension. // Removes the comprehension context then pushes the 'result' sub expression to // the top of the stack. -std::unique_ptr CreateComprehensionFinishStep( - size_t accu_slot); +void EvaluateComprehensionFinishStep(size_t accu_slot, ExecutionFrame& frame); } // namespace google::api::expr::runtime diff --git a/eval/eval/comprehension_step_test.cc b/eval/eval/comprehension_step_test.cc index ef01b034d..ee8b764c1 100644 --- a/eval/eval/comprehension_step_test.cc +++ b/eval/eval/comprehension_step_test.cc @@ -7,7 +7,6 @@ #include "cel/expr/syntax.pb.h" #include "google/protobuf/struct.pb.h" -#include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" @@ -44,7 +43,6 @@ using ::absl_testing::IsOk; using ::absl_testing::StatusIs; using ::cel::BoolValue; using ::cel::Expr; -using ::cel::IdentExpr; using ::cel::IntValue; using ::cel::TypeProvider; using ::cel::Value; @@ -57,12 +55,6 @@ using ::testing::Eq; using ::testing::Return; using ::testing::SizeIs; -IdentExpr CreateIdent(const std::string& var) { - IdentExpr expr; - expr.set_name(var); - return expr; -} - class ListKeysStepTest : public testing::Test { public: ListKeysStepTest() = default; @@ -104,7 +96,8 @@ MATCHER_P(CelStringValue, val, "") { TEST_F(ListKeysStepTest, MapPartiallyUnknown) { ExecutionPath path; path.push_back(ExpressionStep::MakeGenericStep(CreateIdentStep("var"))); - auto init_step = std::make_unique(); + auto init_step = + std::make_unique(/*iter_slot=*/0, /*accu_slot=*/0); init_step->set_error_jump_offset(1); path.push_back(ExpressionStep::MakeGenericStep(std::move(init_step))); path.push_back(ExpressionStep::MakeGenericStep( @@ -141,7 +134,8 @@ TEST_F(ListKeysStepTest, MapPartiallyUnknown) { TEST_F(ListKeysStepTest, ErrorPassedThrough) { ExecutionPath path; path.push_back(ExpressionStep::MakeGenericStep(CreateIdentStep("var"))); - auto init_step = std::make_unique(); + auto init_step = + std::make_unique(/*iter_slot=*/0, /*accu_slot=*/0); init_step->set_error_jump_offset(1); path.push_back(ExpressionStep::MakeGenericStep(std::move(init_step))); path.push_back(ExpressionStep::MakeGenericStep( @@ -165,7 +159,8 @@ TEST_F(ListKeysStepTest, ErrorPassedThrough) { TEST_F(ListKeysStepTest, UnknownSetPassedThrough) { ExecutionPath path; path.push_back(ExpressionStep::MakeGenericStep(CreateIdentStep("var"))); - auto init_step = std::make_unique(); + auto init_step = + std::make_unique(/*iter_slot=*/0, /*accu_slot=*/0); init_step->set_error_jump_offset(1); path.push_back(ExpressionStep::MakeGenericStep(std::move(init_step))); path.push_back(ExpressionStep::MakeGenericStep( diff --git a/eval/eval/evaluator_core.cc b/eval/eval/evaluator_core.cc index 86ae84d3b..308605809 100644 --- a/eval/eval/evaluator_core.cc +++ b/eval/eval/evaluator_core.cc @@ -30,6 +30,8 @@ #include "common/value.h" #include "common/value_kind.h" #include "eval/eval/attribute_trail.h" +#include "eval/eval/comprehension_slots.h" +#include "eval/eval/comprehension_step.h" #include "eval/eval/lazy_init_step.h" #include "eval/eval/logic_step.h" #include "internal/status_macros.h" @@ -108,6 +110,17 @@ class EvaluationStatus final { alignas(absl::Status) char status_[sizeof(absl::Status)]; }; +void EvaluateReadSlotStep(size_t slot_index, ExecutionFrame& frame) { + const ComprehensionSlots::Slot* slot = + frame.comprehension_slots().Get(slot_index); + if (!slot->Has()) { + frame.Abort(absl::InternalError(absl::StrCat( + "Comprehension variable read out of scope: ", slot_index))); + return; + } + frame.value_stack().Push(slot->value(), slot->attribute()); +} + } // namespace void ExpressionStep::Evaluate(ExecutionFrame* context) const { @@ -158,11 +171,31 @@ void ExpressionStep::Evaluate(ExecutionFrame* context) const { case ExpressionStepKind::kBooleanAnd: EvaluateBoolLogicStep(BoolLogicKind::kAnd, u_.arg_count, *context); break; + case ExpressionStepKind::kComprehensionFinish: + EvaluateComprehensionFinishStep(u_.slot_index, *context); + break; + case ExpressionStepKind::kComprehensionNext: + u_.next_step.Evaluate1(context); + break; + case ExpressionStepKind::kComprehensionNext2: + u_.next_step.Evaluate2(context); + break; + case ExpressionStepKind::kComprehensionCond: + u_.cond_step.Evaluate1(context); + break; + case ExpressionStepKind::kComprehensionCond2: + u_.cond_step.Evaluate2(context); + break; + case ExpressionStepKind::kReadSlot: + EvaluateReadSlotStep(u_.slot_index, *context); + break; case ExpressionStepKind::kMovedFrom: - default: context->Abort( absl::InternalError("ExpressionStep::Evaluate called on moved-from " "object")); + break; + default: + ABSL_UNREACHABLE(); } } @@ -313,6 +346,22 @@ bool IsConstant(const ExpressionStep& step) { } } +ComprehensionCondStep* GetIfComprehensionCondStep(ExpressionStep& step) { + if (step.header_.kind == ExpressionStepKind::kComprehensionCond || + step.header_.kind == ExpressionStepKind::kComprehensionCond2) { + return &step.u_.cond_step; + } + return nullptr; +} + +ComprehensionNextStep* GetIfComprehensionNextStep(ExpressionStep& step) { + if (step.header_.kind == ExpressionStepKind::kComprehensionNext || + step.header_.kind == ExpressionStepKind::kComprehensionNext2) { + return &step.u_.next_step; + } + return nullptr; +} + absl::Status WrappedDirectStep::Evaluate(ExecutionFrame* frame) const { cel::Value result; AttributeTrail attribute_trail; diff --git a/eval/eval/evaluator_core.h b/eval/eval/evaluator_core.h index 73772845c..8463f4ad6 100644 --- a/eval/eval/evaluator_core.h +++ b/eval/eval/evaluator_core.h @@ -35,8 +35,10 @@ #include "common/value.h" #include "eval/eval/attribute_utility.h" #include "eval/eval/comprehension_slots.h" +#include "eval/eval/comprehension_step.h" #include "eval/eval/direct_expression_step.h" #include "eval/eval/evaluator_stack.h" +#include "eval/eval/expression_step_logic.h" #include "eval/eval/iterator_stack.h" #include "eval/eval/lazy_init_step.h" #include "eval/eval/logic_step.h" @@ -59,8 +61,6 @@ class ExecutionFrame; using EvaluationListener = cel::TraceableProgram::EvaluationListener; -class ExpressionStepLogic; - enum class ExpressionStepKind : uint16_t { kMovedFrom = 0, kGenericLogic = 1, @@ -78,6 +78,15 @@ enum class ExpressionStepKind : uint16_t { kNotStrictlyFalse = 12, kBooleanOr = 13, kBooleanAnd = 14, + // Comprehension steps. + // Init step doesn't fit inline and is slow anyway, so just use a generic + // step. + kComprehensionFinish = 15, + kComprehensionNext = 16, + kComprehensionCond = 17, + kComprehensionNext2 = 18, + kComprehensionCond2 = 19, + kReadSlot = 20, }; class ExpressionStep { @@ -170,6 +179,44 @@ class ExpressionStep { return step; } + static ExpressionStep MakeComprehensionFinishStep(size_t accu_slot, + int64_t id = -1) { + ExpressionStep step(ExpressionStepKind::kComprehensionFinish, id); + step.u_.slot_index = accu_slot; + return step; + } + + static ExpressionStep MakeComprehensionNextStep(int64_t id = -1) { + ExpressionStep step(ExpressionStepKind::kComprehensionNext, id); + step.u_.next_step = ComprehensionNextStep(); + return step; + } + + static ExpressionStep MakeComprehensionNext2Step(int64_t id = -1) { + ExpressionStep step(ExpressionStepKind::kComprehensionNext2, id); + step.u_.next_step = ComprehensionNextStep(); + return step; + } + + static ExpressionStep MakeComprehensionCondStep(int64_t id = -1) { + ExpressionStep step(ExpressionStepKind::kComprehensionCond, id); + step.u_.cond_step = ComprehensionCondStep(); + return step; + } + + static ExpressionStep MakeComprehensionCond2Step(int64_t id = -1) { + ExpressionStep step(ExpressionStepKind::kComprehensionCond2, id); + step.u_.cond_step = ComprehensionCondStep(); + return step; + } + + static ExpressionStep MakeReadSlotStep(size_t slot_index, int64_t id = -1) { + ExpressionStep step(ExpressionStepKind::kReadSlot, id); + ABSL_DCHECK_LT(slot_index, std::numeric_limits::max()); + step.u_.slot_index = slot_index; + return step; + } + private: struct Header { ExpressionStepKind kind; @@ -201,6 +248,10 @@ class ExpressionStep { friend bool GetIfConstant(const ExpressionStep& step, cel::Value& out); friend bool IsConstant(const ExpressionStep& step); + friend ComprehensionCondStep* GetIfComprehensionCondStep( + ExpressionStep& step); + friend ComprehensionNextStep* GetIfComprehensionNextStep( + ExpressionStep& step); Header header_; // Note: ptr members are 'owned' by the step. @@ -217,6 +268,8 @@ class ExpressionStep { size_t slot_index; ClearSlotStepInfo clear_slots; size_t arg_count; + ComprehensionCondStep cond_step; + ComprehensionNextStep next_step; Data() : empty(nullptr) {} ~Data() {} @@ -232,28 +285,6 @@ class ExpressionStep { static_assert(sizeof(ExpressionStep) == 16); #endif -// Class Expression represents single execution step. -class ExpressionStepLogic { - public: - virtual ~ExpressionStepLogic() = default; - - // Performs actual evaluation. - // Values are passed between Expression objects via EvaluatorStack, which is - // supplied with context. - // Also, Expression gets values supplied by caller though Activation - // interface. - // ExpressionStep instances can in specific cases - // modify execution order(perform jumps). - virtual absl::Status Evaluate(ExecutionFrame* context) const = 0; - - // Return the type of the underlying expression step for special handling in - // the planning phase. This should only be overridden by special cases, and - // callers must not make any assumptions about the default case. - virtual cel::NativeTypeId GetNativeTypeId() const { - return cel::NativeTypeId(); - } -}; - // Wrapper for direct steps to work with the stack machine impl. class WrappedDirectStep : public ExpressionStepLogic { public: @@ -575,6 +606,21 @@ class ExecutionFrame : public ExecutionFrameBase { return absl::OkStatus(); } + void JumpToOrAbort(int offset) { + ABSL_DCHECK_LE(offset, static_cast(execution_path_.size())); + ABSL_DCHECK_GE(offset, -static_cast(pc_)); + + int new_pc = static_cast(pc_) + offset; + if (new_pc < 0 || new_pc > static_cast(execution_path_.size())) { + Abort(absl::Status(absl::StatusCode::kInternal, + absl::StrCat("Jump address out of range: position: ", + pc_, ", offset: ", offset, + ", range: ", execution_path_.size()))); + return; + } + pc_ = static_cast(new_pc); + } + // Move pc to a subexpression. // // Unlike a `Call` in a programming language, the subexpression is evaluated @@ -740,6 +786,9 @@ bool GetIfConstant(const ExpressionStep& step, cel::Value& out); // Checks if the step is a constant. bool IsConstant(const ExpressionStep& step); +ComprehensionCondStep* GetIfComprehensionCondStep(ExpressionStep& step); +ComprehensionNextStep* GetIfComprehensionNextStep(ExpressionStep& step); + // Implementation details. inline ExpressionStep::~ExpressionStep() { @@ -763,6 +812,12 @@ inline ExpressionStep::~ExpressionStep() { case ExpressionStepKind::kNotStrictlyFalse: case ExpressionStepKind::kBooleanOr: case ExpressionStepKind::kBooleanAnd: + case ExpressionStepKind::kComprehensionFinish: + case ExpressionStepKind::kComprehensionCond: + case ExpressionStepKind::kComprehensionNext: + case ExpressionStepKind::kComprehensionCond2: + case ExpressionStepKind::kComprehensionNext2: + case ExpressionStepKind::kReadSlot: break; default: ABSL_UNREACHABLE(); diff --git a/eval/eval/expression_step_base.h b/eval/eval/expression_step_base.h index 565dc5fa5..7f4425098 100644 --- a/eval/eval/expression_step_base.h +++ b/eval/eval/expression_step_base.h @@ -3,7 +3,7 @@ #include -#include "eval/eval/evaluator_core.h" +#include "eval/eval/expression_step_logic.h" namespace google::api::expr::runtime { diff --git a/eval/eval/expression_step_logic.h b/eval/eval/expression_step_logic.h new file mode 100644 index 000000000..2d256636f --- /dev/null +++ b/eval/eval/expression_step_logic.h @@ -0,0 +1,50 @@ +// Copyright 2026 Google LLC +// +// 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 +// +// https://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. + +#ifndef THIRD_PARTY_CEL_CPP_EVAL_EVAL_EXPRESSION_STEP_LOGIC_H_ +#define THIRD_PARTY_CEL_CPP_EVAL_EVAL_EXPRESSION_STEP_LOGIC_H_ + +#include "absl/status/status.h" +#include "common/native_type.h" + +namespace google::api::expr::runtime { + +class ExecutionFrame; + +// ExpressionStepLogic is the base class for generic expression steps that are +// not implemented directly in the evaluator core. +class ExpressionStepLogic { + public: + virtual ~ExpressionStepLogic() = default; + + // Performs actual evaluation. + // Values are passed between Expression objects via EvaluatorStack, which is + // supplied with context. + // Also, Expression gets values supplied by caller though Activation + // interface. + // ExpressionStep instances can in specific cases + // modify execution order(perform jumps). + virtual absl::Status Evaluate(ExecutionFrame* context) const = 0; + + // Return the type of the underlying expression step for special handling in + // the planning phase. This should only be overridden by special cases, and + // callers must not make any assumptions about the default case. + virtual cel::NativeTypeId GetNativeTypeId() const { + return cel::NativeTypeId(); + } +}; + +} // namespace google::api::expr::runtime + +#endif // THIRD_PARTY_CEL_CPP_EVAL_EVAL_EXPRESSION_STEP_LOGIC_H_ diff --git a/eval/eval/ident_step.cc b/eval/eval/ident_step.cc index 9d6edef72..eee511a7e 100644 --- a/eval/eval/ident_step.cc +++ b/eval/eval/ident_step.cc @@ -17,6 +17,7 @@ #include "eval/eval/direct_expression_step.h" #include "eval/eval/evaluator_core.h" #include "eval/eval/expression_step_base.h" +#include "eval/eval/expression_step_logic.h" #include "eval/internal/errors.h" #include "internal/status_macros.h" @@ -94,24 +95,6 @@ absl::StatusOr LookupSlot( return slot; } -class SlotStep : public ExpressionStepBase { - public: - SlotStep(absl::string_view name, size_t slot_index) - : name_(name), slot_index_(slot_index) {} - - absl::Status Evaluate(ExecutionFrame* frame) const override { - CEL_ASSIGN_OR_RETURN(const ComprehensionSlots::Slot* slot, - LookupSlot(name_, slot_index_, *frame)); - frame->value_stack().Push(slot->value(), slot->attribute()); - return absl::OkStatus(); - } - - private: - std::string name_; - - size_t slot_index_; -}; - class DirectIdentStep : public DirectExpressionStep { public: DirectIdentStep(absl::string_view name, int64_t expr_id) @@ -167,9 +150,4 @@ std::unique_ptr CreateIdentStep( return std::make_unique(name); } -std::unique_ptr CreateIdentStepForSlot( - const absl::string_view name, size_t slot_index) { - return std::make_unique(name, slot_index); -} - } // namespace google::api::expr::runtime diff --git a/eval/eval/ident_step.h b/eval/eval/ident_step.h index ad65478bd..508056827 100644 --- a/eval/eval/ident_step.h +++ b/eval/eval/ident_step.h @@ -5,10 +5,9 @@ #include #include -#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "eval/eval/direct_expression_step.h" -#include "eval/eval/evaluator_core.h" +#include "eval/eval/expression_step_logic.h" namespace google::api::expr::runtime { @@ -21,10 +20,6 @@ std::unique_ptr CreateDirectSlotIdentStep( // Factory method for Ident - based Execution step std::unique_ptr CreateIdentStep(absl::string_view name); -// Factory method for identifier that has been assigned to a slot. -std::unique_ptr CreateIdentStepForSlot( - absl::string_view name, size_t slot_index); - } // namespace google::api::expr::runtime #endif // THIRD_PARTY_CEL_CPP_EVAL_EVAL_IDENT_STEP_H_ diff --git a/eval/eval/iterator_stack.h b/eval/eval/iterator_stack.h index 9b5daa889..0b0de14aa 100644 --- a/eval/eval/iterator_stack.h +++ b/eval/eval/iterator_stack.h @@ -27,8 +27,15 @@ namespace cel::runtime_internal { class IteratorStack final { public: + struct Entry { + absl_nonnull ValueIteratorPtr iterator; + size_t iter_slot; + size_t iter2_slot; + size_t accu_slot; + }; + explicit IteratorStack(size_t max_size) : max_size_(max_size) { - iterators_.reserve(max_size_); + entries_.reserve(max_size_); } IteratorStack(const IteratorStack&) = delete; @@ -37,38 +44,55 @@ class IteratorStack final { IteratorStack& operator=(const IteratorStack&) = delete; IteratorStack& operator=(IteratorStack&&) = delete; - size_t size() const { return iterators_.size(); } + size_t size() const { return entries_.size(); } - bool empty() const { return iterators_.empty(); } + bool empty() const { return entries_.empty(); } - bool full() const { return iterators_.size() == max_size_; } + bool full() const { return entries_.size() == max_size_; } size_t max_size() const { return max_size_; } - void Clear() { iterators_.clear(); } + void Clear() { entries_.clear(); } + + void Push(absl_nonnull ValueIteratorPtr iterator, size_t iter_slot, + size_t iter2_slot, size_t accu_slot) { + ABSL_DCHECK(!full()); + ABSL_DCHECK(iterator != nullptr); + + entries_.push_back( + Entry{std::move(iterator), iter_slot, iter2_slot, accu_slot}); + } - void Push(absl_nonnull ValueIteratorPtr iterator) { + void Push(absl_nonnull ValueIteratorPtr iterator, size_t iter_slot, + size_t accu_slot) { ABSL_DCHECK(!full()); ABSL_DCHECK(iterator != nullptr); - iterators_.push_back(std::move(iterator)); + entries_.push_back(Entry{std::move(iterator), iter_slot, 0, accu_slot}); + } + + ValueIterator* absl_nonnull PeekIterator() { + ABSL_DCHECK(!empty()); + + return entries_.back().iterator.get(); } - ValueIterator* absl_nonnull Peek() { + // Returns a pointer to the top entry in the stack. + // Invalidated by Pop() and Push(). + Entry* absl_nonnull Peek() { ABSL_DCHECK(!empty()); - ABSL_DCHECK(iterators_.back() != nullptr); - return iterators_.back().get(); + return &entries_.back(); } void Pop() { ABSL_DCHECK(!empty()); - iterators_.pop_back(); + entries_.pop_back(); } private: - std::vector iterators_; + std::vector entries_; size_t max_size_; };