| 183 | } |
| 184 | |
| 185 | absl::Status ConstantFoldingExtension::OnPostVisit(PlannerContext& context, |
| 186 | const Expr& node) { |
| 187 | if (is_const_.empty()) { |
| 188 | return absl::InternalError("ConstantFoldingExtension called out of order."); |
| 189 | } |
| 190 | |
| 191 | IsConst is_const = is_const_.back(); |
| 192 | is_const_.pop_back(); |
| 193 | |
| 194 | if (is_const == IsConst::kNonConst) { |
| 195 | // update parent |
| 196 | if (!is_const_.empty()) { |
| 197 | is_const_.back() = IsConst::kNonConst; |
| 198 | } |
| 199 | return absl::OkStatus(); |
| 200 | } |
| 201 | ExecutionPathView subplan = context.GetSubplan(node); |
| 202 | if (subplan.empty()) { |
| 203 | // This subexpression is already optimized out or suppressed. |
| 204 | return absl::OkStatus(); |
| 205 | } |
| 206 | // copy string to managed handle if backed by the original program. |
| 207 | Value value; |
| 208 | if (node.has_const_expr()) { |
| 209 | CEL_ASSIGN_OR_RETURN(value, |
| 210 | ConvertConstant(node.const_expr(), state_.arena())); |
| 211 | } else { |
| 212 | ExecutionFrame frame(subplan, empty_, context.options(), state_); |
| 213 | state_.Reset(); |
| 214 | // Update stack size to accommodate sub expression. |
| 215 | // This only results in a vector resize if the new maxsize is greater than |
| 216 | // the current capacity. |
| 217 | state_.value_stack().SetMaxSize(subplan.size()); |
| 218 | |
| 219 | auto result = frame.Evaluate(); |
| 220 | // If this would be a runtime error, then don't adjust the program plan, but |
| 221 | // rather allow the error to occur at runtime to preserve the evaluation |
| 222 | // contract with non-constant folding use cases. |
| 223 | if (!result.ok()) { |
| 224 | return absl::OkStatus(); |
| 225 | } |
| 226 | value = *result; |
| 227 | if (value->Is<UnknownValue>()) { |
| 228 | return absl::OkStatus(); |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | // If recursive planning enabled (recursion limit unbounded or at least 1), |
| 233 | // use a recursive (direct) step for the folded constant. |
| 234 | // |
| 235 | // Constant folding is applied leaf to root based on the program plan so far, |
| 236 | // so the planner will have an opportunity to validate that the recursion |
| 237 | // limit is being followed when visiting parent nodes in the AST. |
| 238 | if (context.options().max_recursion_depth != 0) { |
| 239 | return context.ReplaceSubplan( |
| 240 | node, CreateConstValueDirectStep(std::move(value), node.id()), 1); |
| 241 | } |
| 242 | |