Coverage Report

Created: 2026-07-11 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/eval/compiler/flat_expr_builder.cc
Line
Count
Source
1
/*
2
 * Copyright 2021 Google LLC
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include "eval/compiler/flat_expr_builder.h"
18
19
#include <algorithm>
20
#include <cstddef>
21
#include <cstdint>
22
#include <deque>
23
#include <iterator>
24
#include <limits>
25
#include <memory>
26
#include <stack>
27
#include <string>
28
#include <type_traits>
29
#include <utility>
30
#include <vector>
31
32
#include "absl/algorithm/container.h"
33
#include "absl/base/attributes.h"
34
#include "absl/base/optimization.h"
35
#include "absl/container/flat_hash_map.h"
36
#include "absl/container/flat_hash_set.h"
37
#include "absl/container/node_hash_map.h"
38
#include "absl/functional/any_invocable.h"
39
#include "absl/log/absl_check.h"
40
#include "absl/log/check.h"
41
#include "absl/status/status.h"
42
#include "absl/status/statusor.h"
43
#include "absl/strings/match.h"
44
#include "absl/strings/numbers.h"
45
#include "absl/strings/str_cat.h"
46
#include "absl/strings/string_view.h"
47
#include "absl/strings/strip.h"
48
#include "absl/types/optional.h"
49
#include "absl/types/span.h"
50
#include "absl/types/variant.h"
51
#include "base/ast.h"
52
#include "base/builtins.h"
53
#include "base/type_provider.h"
54
#include "common/allocator.h"
55
#include "common/ast.h"
56
#include "common/ast_traverse.h"
57
#include "common/ast_visitor.h"
58
#include "common/constant.h"
59
#include "common/expr.h"
60
#include "common/kind.h"
61
#include "common/type.h"
62
#include "common/value.h"
63
#include "eval/compiler/check_ast_extensions.h"
64
#include "eval/compiler/flat_expr_builder_extensions.h"
65
#include "eval/compiler/resolver.h"
66
#include "eval/eval/comprehension_step.h"
67
#include "eval/eval/const_value_step.h"
68
#include "eval/eval/container_access_step.h"
69
#include "eval/eval/create_list_step.h"
70
#include "eval/eval/create_map_step.h"
71
#include "eval/eval/create_struct_step.h"
72
#include "eval/eval/direct_expression_step.h"
73
#include "eval/eval/equality_steps.h"
74
#include "eval/eval/evaluator_core.h"
75
#include "eval/eval/function_step.h"
76
#include "eval/eval/ident_step.h"
77
#include "eval/eval/jump_step.h"
78
#include "eval/eval/lazy_init_step.h"
79
#include "eval/eval/logic_step.h"
80
#include "eval/eval/optional_or_step.h"
81
#include "eval/eval/select_step.h"
82
#include "eval/eval/shadowable_value_step.h"
83
#include "eval/eval/ternary_step.h"
84
#include "eval/eval/trace_step.h"
85
#include "internal/status_macros.h"
86
#include "runtime/internal/convert_constant.h"
87
#include "runtime/internal/issue_collector.h"
88
#include "runtime/runtime_issue.h"
89
#include "runtime/runtime_options.h"
90
#include "runtime/type_registry.h"
91
#include "google/protobuf/arena.h"
92
93
namespace google::api::expr::runtime {
94
95
namespace {
96
97
using ::cel::Ast;
98
using ::cel::AstTraverse;
99
using ::cel::RuntimeIssue;
100
using ::cel::StringValue;
101
using ::cel::Value;
102
using ::cel::runtime_internal::ConvertConstant;
103
using ::cel::runtime_internal::GetLegacyRuntimeTypeProvider;
104
using ::cel::runtime_internal::GetRuntimeTypeProvider;
105
using ::cel::runtime_internal::IssueCollector;
106
107
constexpr absl::string_view kOptionalOrFn = "or";
108
constexpr absl::string_view kOptionalOrValueFn = "orValue";
109
constexpr absl::string_view kBlock = "cel.@block";
110
111
// Forward declare to resolve circular dependency for short_circuiting visitors.
112
class FlatExprVisitor;
113
114
// Error code for failed recursive program building. Generally indicates an
115
// optimization doesn't support recursive programs.
116
0
absl::Status FailedRecursivePlanning() {
117
0
  return absl::InternalError(
118
0
      "failed to build recursive program. check for unsupported optimizations");
119
0
}
120
121
// Helper for bookkeeping variables mapped to indexes.
122
class IndexManager {
123
 public:
124
10.7k
  IndexManager() : next_free_slot_(0), max_slot_count_(0) {}
125
126
4.11k
  size_t ReserveSlots(size_t n) {
127
4.11k
    size_t result = next_free_slot_;
128
4.11k
    next_free_slot_ += n;
129
4.11k
    if (next_free_slot_ > max_slot_count_) {
130
3.67k
      max_slot_count_ = next_free_slot_;
131
3.67k
    }
132
4.11k
    return result;
133
4.11k
  }
134
135
2.85k
  size_t ReleaseSlots(size_t n) {
136
2.85k
    next_free_slot_ -= n;
137
2.85k
    return next_free_slot_;
138
2.85k
  }
139
140
10.3k
  size_t max_slot_count() const { return max_slot_count_; }
141
142
 private:
143
  size_t next_free_slot_;
144
  size_t max_slot_count_;
145
};
146
147
// Helper for computing jump offsets.
148
//
149
// Jumps should be self-contained to a single expression node -- jumping
150
// outside that range is a bug.
151
struct ProgramStepIndex {
152
  int index;
153
  ProgramBuilder::Subexpression* subexpression;
154
};
155
156
// A convenience wrapper for offset-calculating logic.
157
class Jump {
158
 public:
159
  // Default constructor for empty jump.
160
  //
161
  // Users must check that jump is non-empty before calling member functions.
162
1.71k
  explicit Jump() : self_index_{-1, nullptr}, jump_step_(nullptr) {}
163
  Jump(ProgramStepIndex self_index, JumpStepBase* jump_step)
164
11.1k
      : self_index_(self_index), jump_step_(jump_step) {}
165
166
  static absl::StatusOr<int> CalculateOffset(ProgramStepIndex base,
167
25.2k
                                             ProgramStepIndex target) {
168
25.2k
    if (target.subexpression != base.subexpression) {
169
0
      return absl::InternalError(
170
0
          "Jump target must be contained in the parent"
171
0
          "subexpression");
172
0
    }
173
174
25.2k
    int offset = base.subexpression->CalculateOffset(base.index, target.index);
175
25.2k
    return offset;
176
25.2k
  }
177
178
10.9k
  absl::Status set_target(ProgramStepIndex target) {
179
10.9k
    CEL_ASSIGN_OR_RETURN(int offset, CalculateOffset(self_index_, target));
180
181
10.9k
    jump_step_->set_jump_offset(offset);
182
10.9k
    return absl::OkStatus();
183
10.9k
  }
184
185
1.51k
  bool exists() { return jump_step_ != nullptr; }
186
187
 private:
188
  ProgramStepIndex self_index_;
189
  JumpStepBase* jump_step_;
190
};
191
192
class CondVisitor {
193
 public:
194
7.17k
  virtual ~CondVisitor() = default;
195
  virtual void PreVisit(const cel::Expr* expr) = 0;
196
  virtual void PostVisitArg(int arg_num, const cel::Expr* expr) = 0;
197
  virtual void PostVisit(const cel::Expr* expr) = 0;
198
0
  virtual void PostVisitTarget(const cel::Expr* expr) {}
199
};
200
201
// Visitor managing the "&&" and "||" (boolean logic) operations.
202
// Implements short-circuiting if enabled.
203
//
204
// With short-circuiting enabled, generates a program like:
205
//   +-------------+------------------------+-----------------------+
206
//   | PC          | Step                   | Stack                 |
207
//   +-------------+------------------------+-----------------------+
208
//   | i + 0       | <Arg1>                 | arg1                  |
209
//   | i + 1       | ConditionalJump i + 4  | arg1                  |
210
//   | i + 2       | <Arg2>                 | arg1, arg2            |
211
//   | i + 3       | BooleanOperator        | Op(arg1, arg2)        |
212
//   | i + 4       | <rest of program>      | arg1 | Op(arg1, arg2) |
213
//   +-------------+------------------------+------------------------+
214
class LogicalCondVisitor : public CondVisitor {
215
 public:
216
  explicit LogicalCondVisitor(FlatExprVisitor* visitor, bool is_or,
217
                              bool short_circuiting)
218
6.60k
      : visitor_(visitor), is_or_(is_or), short_circuiting_(short_circuiting) {}
219
220
  void PreVisit(const cel::Expr* expr) override;
221
  void PostVisitArg(int arg_num, const cel::Expr* expr) override;
222
  void PostVisit(const cel::Expr* expr) override;
223
224
 private:
225
  FlatExprVisitor* visitor_;
226
  const bool is_or_;
227
  std::vector<Jump> jump_steps_;
228
  bool short_circuiting_;
229
};
230
231
// Visitor managing optional "or" and "orValue" operations.
232
// Implements short-circuiting if enabled.
233
class OptionalOrCondVisitor : public CondVisitor {
234
 public:
235
  explicit OptionalOrCondVisitor(FlatExprVisitor* visitor, bool is_or_value,
236
                                 bool short_circuiting)
237
0
      : visitor_(visitor),
238
0
        is_or_value_(is_or_value),
239
0
        short_circuiting_(short_circuiting) {}
240
241
  void PreVisit(const cel::Expr* expr) override;
242
0
  void PostVisitArg(int arg_num, const cel::Expr* expr) override {}
243
  void PostVisitTarget(const cel::Expr* expr) override;
244
  void PostVisit(const cel::Expr* expr) override;
245
246
 private:
247
  FlatExprVisitor* visitor_;
248
  const bool is_or_value_;
249
  std::vector<Jump> jump_steps_;
250
  bool short_circuiting_;
251
};
252
253
class TernaryCondVisitor : public CondVisitor {
254
 public:
255
572
  explicit TernaryCondVisitor(FlatExprVisitor* visitor) : visitor_(visitor) {}
256
257
  void PreVisit(const cel::Expr* expr) override;
258
  void PostVisitArg(int arg_num, const cel::Expr* expr) override;
259
  void PostVisit(const cel::Expr* expr) override;
260
261
 private:
262
  FlatExprVisitor* visitor_;
263
  Jump jump_to_second_;
264
  Jump error_jump_;
265
  Jump jump_after_first_;
266
};
267
268
class ExhaustiveTernaryCondVisitor : public CondVisitor {
269
 public:
270
  explicit ExhaustiveTernaryCondVisitor(FlatExprVisitor* visitor)
271
0
      : visitor_(visitor) {}
272
273
  void PreVisit(const cel::Expr* expr) override;
274
0
  void PostVisitArg(int arg_num, const cel::Expr* expr) override {}
275
  void PostVisit(const cel::Expr* expr) override;
276
277
 private:
278
  FlatExprVisitor* visitor_;
279
};
280
281
// Returns a hint for the number of program nodes (steps or subexpressions) that
282
// will be created for this expr.
283
334k
size_t SizeHint(const cel::Expr& expr) {
284
334k
  switch (expr.kind_case()) {
285
123k
    case cel::ExprKindCase::kConstant:
286
123k
      return 1;
287
54.3k
    case cel::ExprKindCase::kIdentExpr:
288
54.3k
      return 1;
289
11.5k
    case cel::ExprKindCase::kSelectExpr:
290
11.5k
      return 2;
291
117k
    case cel::ExprKindCase::kCallExpr:
292
117k
      return expr.call_expr().args().size() +
293
117k
             (expr.call_expr().has_target() ? 2 : 1);
294
16.7k
    case cel::ExprKindCase::kListExpr:
295
16.7k
      return expr.list_expr().elements().size() + 1;
296
1.71k
    case cel::ExprKindCase::kStructExpr:
297
1.71k
      return expr.struct_expr().fields().size() + 1;
298
5.66k
    case cel::ExprKindCase::kMapExpr:
299
5.66k
      return 2 * expr.struct_expr().fields().size() + 1;
300
4.11k
    default:
301
4.11k
      return 1;
302
334k
  }
303
0
  return 0;
304
334k
}
305
306
// Returns whether this comprehension appears to be a standard map/filter
307
// macro implementation. It is not exhaustive, so it is unsafe to use with
308
// custom comprehensions outside of the standard macros or hand crafted ASTs.
309
bool IsOptimizableListAppend(const cel::ComprehensionExpr* comprehension,
310
4.11k
                             bool enable_comprehension_list_append) {
311
4.11k
  if (!enable_comprehension_list_append) {
312
4.11k
    return false;
313
4.11k
  }
314
0
  absl::string_view accu_var = comprehension->accu_var();
315
0
  if (accu_var.empty() ||
316
0
      comprehension->result().ident_expr().name() != accu_var) {
317
0
    return false;
318
0
  }
319
0
  if (!comprehension->accu_init().has_list_expr() ||
320
0
      !comprehension->accu_init().list_expr().elements().empty()) {
321
0
    return false;
322
0
  }
323
324
0
  if (!comprehension->loop_step().has_call_expr()) {
325
0
    return false;
326
0
  }
327
328
  // Macro loop_step for a filter() will contain a ternary:
329
  //   filter ? accu_var + [elem] : accu_var
330
  // Macro loop_step for a map() will contain a list concat operation:
331
  //   accu_var + [elem]
332
0
  const auto* call_expr = &comprehension->loop_step().call_expr();
333
334
0
  if (call_expr->function() == cel::builtin::kTernary &&
335
0
      call_expr->args().size() == 3) {
336
0
    if (!call_expr->args()[1].has_call_expr()) {
337
0
      return false;
338
0
    }
339
0
    call_expr = &(call_expr->args()[1].call_expr());
340
0
  }
341
342
0
  return call_expr->function() == cel::builtin::kAdd &&
343
0
         call_expr->args().size() == 2 &&
344
0
         call_expr->args()[0].has_ident_expr() &&
345
0
         call_expr->args()[0].ident_expr().name() == accu_var &&
346
0
         call_expr->args()[1].has_list_expr() &&
347
0
         call_expr->args()[1].list_expr().elements().size() == 1;
348
0
}
349
350
// Assuming `IsOptimizableListAppend()` return true, return a pointer to the
351
// call `accu_var + [elem]`.
352
const cel::CallExpr* GetOptimizableListAppendCall(
353
0
    const cel::ComprehensionExpr* comprehension) {
354
0
  ABSL_DCHECK(IsOptimizableListAppend(
355
0
      comprehension, /*enable_comprehension_list_append=*/true));
356
357
  // Macro loop_step for a filter() will contain a ternary:
358
  //   filter ? accu_var + [elem] : accu_var
359
  // Macro loop_step for a map() will contain a list concat operation:
360
  //   accu_var + [elem]
361
0
  const auto* call_expr = &comprehension->loop_step().call_expr();
362
363
0
  if (call_expr->function() == cel::builtin::kTernary &&
364
0
      call_expr->args().size() == 3) {
365
0
    call_expr = &(call_expr->args()[1].call_expr());
366
0
  }
367
0
  return call_expr;
368
0
}
369
370
// Assuming `IsOptimizableListAppend()` return true, return a pointer to the
371
// node `[elem]`.
372
const cel::Expr* GetOptimizableListAppendOperand(
373
0
    const cel::ComprehensionExpr* comprehension) {
374
0
  return &GetOptimizableListAppendCall(comprehension)->args()[1];
375
0
}
376
377
// Returns whether this comprehension appears to be a macro implementation for
378
// map transformations. It is not exhaustive, so it is unsafe to use with custom
379
// comprehensions outside of the standard macros or hand crafted ASTs.
380
bool IsOptimizableMapInsert(const cel::ComprehensionExpr* comprehension,
381
4.11k
                            bool enable_comprehension_mutable_map) {
382
4.11k
  if (!enable_comprehension_mutable_map) {
383
4.11k
    return false;
384
4.11k
  }
385
0
  if (comprehension->iter_var().empty() || comprehension->iter_var2().empty()) {
386
0
    return false;
387
0
  }
388
0
  absl::string_view accu_var = comprehension->accu_var();
389
0
  if (accu_var.empty() || !comprehension->has_result() ||
390
0
      !comprehension->result().has_ident_expr() ||
391
0
      comprehension->result().ident_expr().name() != accu_var) {
392
0
    return false;
393
0
  }
394
0
  if (!comprehension->accu_init().has_map_expr()) {
395
0
    return false;
396
0
  }
397
0
  if (!comprehension->loop_step().has_call_expr()) {
398
0
    return false;
399
0
  }
400
0
  const auto* call_expr = &comprehension->loop_step().call_expr();
401
402
0
  if (call_expr->function() == cel::builtin::kTernary &&
403
0
      call_expr->args().size() == 3) {
404
0
    if (!call_expr->args()[1].has_call_expr()) {
405
0
      return false;
406
0
    }
407
0
    call_expr = &(call_expr->args()[1].call_expr());
408
0
  }
409
0
  return call_expr->function() == "cel.@mapInsert" &&
410
0
         (call_expr->args().size() == 2 || call_expr->args().size() == 3) &&
411
0
         call_expr->args()[0].has_ident_expr() &&
412
0
         call_expr->args()[0].ident_expr().name() == accu_var;
413
0
}
414
415
4.11k
bool IsBind(const cel::ComprehensionExpr* comprehension) {
416
4.11k
  static constexpr absl::string_view kUnusedIterVar = "#unused";
417
418
4.11k
  return comprehension->loop_condition().const_expr().has_bool_value() &&
419
4.11k
         comprehension->loop_condition().const_expr().bool_value() == false &&
420
0
         comprehension->iter_var() == kUnusedIterVar &&
421
0
         comprehension->iter_var2().empty() &&
422
0
         comprehension->iter_range().has_list_expr() &&
423
0
         comprehension->iter_range().list_expr().elements().empty();
424
4.11k
}
425
426
110k
bool IsBlock(const cel::CallExpr* call) { return call->function() == kBlock; }
427
428
// Visitor for Comprehension expressions.
429
class ComprehensionVisitor {
430
 public:
431
  explicit ComprehensionVisitor(FlatExprVisitor* visitor, bool short_circuiting,
432
                                bool is_trivial, size_t iter_slot,
433
                                size_t iter2_slot, size_t accu_slot)
434
4.11k
      : visitor_(visitor),
435
4.11k
        next_step_(nullptr),
436
4.11k
        cond_step_(nullptr),
437
4.11k
        short_circuiting_(short_circuiting),
438
4.11k
        is_trivial_(is_trivial),
439
4.11k
        accu_init_extracted_(false),
440
4.11k
        iter_slot_(iter_slot),
441
4.11k
        iter2_slot_(iter2_slot),
442
4.11k
        accu_slot_(accu_slot) {}
443
444
  void PreVisit(const cel::Expr* expr);
445
  absl::Status PostVisitArg(cel::ComprehensionArg arg_num,
446
14.3k
                            const cel::Expr* comprehension_expr) {
447
14.3k
    if (is_trivial_) {
448
0
      PostVisitArgTrivial(arg_num, comprehension_expr);
449
0
      return absl::OkStatus();
450
14.3k
    } else {
451
14.3k
      return PostVisitArgDefault(arg_num, comprehension_expr);
452
14.3k
    }
453
14.3k
  }
454
  void PostVisit(const cel::Expr* expr);
455
456
0
  void MarkAccuInitExtracted() { accu_init_extracted_ = true; }
457
458
 private:
459
  void PostVisitArgTrivial(cel::ComprehensionArg arg_num,
460
                           const cel::Expr* comprehension_expr);
461
462
  absl::Status PostVisitArgDefault(cel::ComprehensionArg arg_num,
463
                                   const cel::Expr* comprehension_expr);
464
465
  FlatExprVisitor* visitor_;
466
  ComprehensionInitStep* init_step_;
467
  ComprehensionNextStep* next_step_;
468
  ComprehensionCondStep* cond_step_;
469
  ProgramStepIndex init_step_pos_;
470
  ProgramStepIndex next_step_pos_;
471
  ProgramStepIndex cond_step_pos_;
472
  bool short_circuiting_;
473
  bool is_trivial_;
474
  bool accu_init_extracted_;
475
  size_t iter_slot_;
476
  size_t iter2_slot_;
477
  size_t accu_slot_;
478
};
479
480
absl::flat_hash_set<int32_t> MakeOptionalIndicesSet(
481
0
    const cel::ListExpr& create_list_expr) {
482
0
  absl::flat_hash_set<int32_t> optional_indices;
483
0
  for (size_t i = 0; i < create_list_expr.elements().size(); ++i) {
484
0
    if (create_list_expr.elements()[i].optional()) {
485
0
      optional_indices.insert(static_cast<int32_t>(i));
486
0
    }
487
0
  }
488
0
  return optional_indices;
489
0
}
490
491
absl::flat_hash_set<int32_t> MakeOptionalIndicesSet(
492
1.48k
    const cel::StructExpr& create_struct_expr) {
493
1.48k
  absl::flat_hash_set<int32_t> optional_indices;
494
1.48k
  for (size_t i = 0; i < create_struct_expr.fields().size(); ++i) {
495
0
    if (create_struct_expr.fields()[i].optional()) {
496
0
      optional_indices.insert(static_cast<int32_t>(i));
497
0
    }
498
0
  }
499
1.48k
  return optional_indices;
500
1.48k
}
501
502
absl::flat_hash_set<int32_t> MakeOptionalIndicesSet(
503
6.03k
    const cel::MapExpr& map_expr) {
504
6.03k
  absl::flat_hash_set<int32_t> optional_indices;
505
10.5k
  for (size_t i = 0; i < map_expr.entries().size(); ++i) {
506
4.51k
    if (map_expr.entries()[i].optional()) {
507
0
      optional_indices.insert(static_cast<int32_t>(i));
508
0
    }
509
4.51k
  }
510
6.03k
  return optional_indices;
511
6.03k
}
512
513
class FlatExprVisitor : public cel::AstVisitor {
514
 public:
515
  enum class CallHandlerResult {
516
    // The call was intercepted, no additional processing is needed.
517
    kIntercepted,
518
    // The call was not intercepted, continue with the default processing.
519
    kNotIntercepted,
520
  };
521
522
  // Handler for functions with builtin implementations.
523
  // This is used to replace the usual dispatcher step that applies
524
  // the arguments to a candidate function from the function registry.
525
  using CallHandler = absl::AnyInvocable<CallHandlerResult(
526
      const cel::Expr&, const cel::CallExpr&)>;
527
528
  FlatExprVisitor(
529
      const Resolver& resolver, const cel::RuntimeOptions& options,
530
      std::vector<std::unique_ptr<ProgramOptimizer>> program_optimizers,
531
      const absl::flat_hash_map<int64_t, cel::Reference>& reference_map,
532
      const cel::TypeProvider& type_provider, IssueCollector& issue_collector,
533
      ProgramBuilder& program_builder, PlannerContext& extension_context,
534
      bool enable_optional_types)
535
10.7k
      : resolver_(resolver),
536
10.7k
        type_provider_(type_provider),
537
10.7k
        progress_status_(absl::OkStatus()),
538
10.7k
        resolved_select_expr_(nullptr),
539
10.7k
        options_(options),
540
10.7k
        program_optimizers_(std::move(program_optimizers)),
541
10.7k
        issue_collector_(issue_collector),
542
10.7k
        program_builder_(program_builder),
543
10.7k
        extension_context_(extension_context),
544
10.7k
        enable_optional_types_(enable_optional_types) {
545
10.7k
    constexpr size_t kCallHandlerSizeHint = 11;
546
10.7k
    call_handlers_.reserve(kCallHandlerSizeHint);
547
10.7k
    call_handlers_[cel::builtin::kIndex] = [this](const cel::Expr& expr,
548
10.7k
                                                  const cel::CallExpr& call) {
549
3.94k
      return HandleIndex(expr, call);
550
3.94k
    };
551
10.7k
    call_handlers_[kBlock] = [this](const cel::Expr& expr,
552
10.7k
                                    const cel::CallExpr& call) {
553
0
      return HandleBlock(expr, call);
554
0
    };
555
10.7k
    call_handlers_[cel::builtin::kAdd] = [this](const cel::Expr& expr,
556
10.7k
                                                const cel::CallExpr& call) {
557
9.22k
      return HandleListAppend(expr, call);
558
9.22k
    };
559
10.7k
    if (options_.enable_fast_builtins) {
560
10.7k
      call_handlers_[cel::builtin::kNotStrictlyFalse] =
561
10.7k
          [this](const cel::Expr& expr, const cel::CallExpr& call) {
562
0
            return HandleNotStrictlyFalse(expr, call);
563
0
          };
564
10.7k
      call_handlers_[cel::builtin::kNotStrictlyFalseDeprecated] =
565
10.7k
          [this](const cel::Expr& expr, const cel::CallExpr& call) {
566
0
            return HandleNotStrictlyFalse(expr, call);
567
0
          };
568
10.7k
      call_handlers_[cel::builtin::kNot] = [this](const cel::Expr& expr,
569
10.7k
                                                  const cel::CallExpr& call) {
570
1.02k
        return HandleNot(expr, call);
571
1.02k
      };
572
10.7k
      if (options_.enable_heterogeneous_equality) {
573
10.7k
        for (const auto& in_op :
574
10.7k
             {cel::builtin::kIn, cel::builtin::kInDeprecated,
575
32.3k
              cel::builtin::kInFunction}) {
576
32.3k
          call_handlers_[in_op] = [this](const cel::Expr& expr,
577
32.3k
                                         const cel::CallExpr& call) {
578
2.88k
            return HandleHeterogeneousEqualityIn(expr, call);
579
2.88k
          };
580
32.3k
        }
581
        // Try to detect if the environment is setup with a custom equality
582
        // implementation.
583
10.7k
        if (resolver_
584
10.7k
                .FindOverloads(cel::builtin::kEqual,
585
10.7k
                               /*receiver_style=*/false,
586
10.7k
                               {cel::Kind::kAny, cel::Kind::kAny})
587
10.7k
                .empty()) {
588
10.7k
          call_handlers_[cel::builtin::kEqual] =
589
10.7k
              [this](const cel::Expr& expr, const cel::CallExpr& call) {
590
5.74k
                return HandleHeterogeneousEquality(expr, call,
591
5.74k
                                                   /*inequality=*/false);
592
5.74k
              };
593
10.7k
          call_handlers_[cel::builtin::kInequal] =
594
10.7k
              [this](const cel::Expr& expr, const cel::CallExpr& call) {
595
465
                return HandleHeterogeneousEquality(expr, call,
596
465
                                                   /*inequality=*/true);
597
465
              };
598
10.7k
        }
599
10.7k
      }
600
10.7k
    }
601
10.7k
  }
602
603
0
  void SetMaxRecursionDepth(int max_recursion_depth) {
604
0
    max_recursion_depth_ = max_recursion_depth;
605
0
  }
606
607
182k
  bool PlanRecursiveProgram() const { return max_recursion_depth_ > 0; }
608
609
350k
  void PreVisitExpr(const cel::Expr& expr) override {
610
350k
    ValidateOrError(!absl::holds_alternative<cel::UnspecifiedExpr>(expr.kind()),
611
350k
                    "Invalid empty expression");
612
350k
    if (!progress_status_.ok()) {
613
16.1k
      return;
614
16.1k
    }
615
334k
    if (resume_from_suppressed_branch_ == nullptr &&
616
334k
        suppressed_branches_.find(&expr) != suppressed_branches_.end()) {
617
0
      resume_from_suppressed_branch_ = &expr;
618
0
    }
619
620
334k
    if (block_.has_value()) {
621
0
      BlockInfo& block = *block_;
622
0
      if (block.in && block.bindings_set.contains(&expr)) {
623
0
        block.current_binding = &expr;
624
0
      }
625
0
    }
626
627
334k
    auto* subexpression =
628
334k
        program_builder_.EnterSubexpression(&expr, SizeHint(expr));
629
334k
    if (subexpression == nullptr) {
630
0
      progress_status_.Update(
631
0
          absl::InternalError("same CEL expr visited twice"));
632
0
      return;
633
0
    }
634
635
334k
    for (const std::unique_ptr<ProgramOptimizer>& optimizer :
636
334k
         program_optimizers_) {
637
0
      absl::Status status = optimizer->OnPreVisit(extension_context_, expr);
638
0
      if (!status.ok()) {
639
0
        SetProgressStatusIfError(status);
640
0
      }
641
0
    }
642
334k
  }
643
644
350k
  void PostVisitExpr(const cel::Expr& expr) override {
645
350k
    if (!progress_status_.ok()) {
646
19.7k
      return;
647
19.7k
    }
648
331k
    if (&expr == resume_from_suppressed_branch_) {
649
0
      resume_from_suppressed_branch_ = nullptr;
650
0
    }
651
652
331k
    for (const std::unique_ptr<ProgramOptimizer>& optimizer :
653
331k
         program_optimizers_) {
654
0
      absl::Status status = optimizer->OnPostVisit(extension_context_, expr);
655
0
      if (!status.ok()) {
656
0
        SetProgressStatusIfError(status);
657
0
        return;
658
0
      }
659
0
    }
660
661
331k
    auto* subexpression = program_builder_.current();
662
331k
    if (subexpression != nullptr && options_.enable_recursive_tracing &&
663
0
        subexpression->IsRecursive()) {
664
0
      auto program = subexpression->ExtractRecursiveProgram();
665
0
      subexpression->set_recursive_program(
666
0
          std::make_unique<TraceStep>(std::move(program.step)), program.depth);
667
0
    }
668
669
331k
    program_builder_.ExitSubexpression(&expr);
670
671
331k
    if (!comprehension_stack_.empty() &&
672
124k
        comprehension_stack_.back().is_optimizable_bind &&
673
0
        (&comprehension_stack_.back().comprehension->accu_init() == &expr)) {
674
0
      SetProgressStatusIfError(
675
0
          MaybeExtractSubexpression(&expr, comprehension_stack_.back()));
676
0
    }
677
678
331k
    if (block_.has_value()) {
679
0
      BlockInfo& block = *block_;
680
0
      if (block.current_binding == &expr) {
681
0
        int index = program_builder_.ExtractSubexpression(&expr);
682
0
        if (index == -1) {
683
0
          SetProgressStatusIfError(
684
0
              absl::InvalidArgumentError("failed to extract subexpression"));
685
0
          return;
686
0
        }
687
0
        block.subexpressions[block.current_index++] = index;
688
0
        block.current_binding = nullptr;
689
0
      }
690
0
    }
691
331k
  }
692
693
  void PostVisitConst(const cel::Expr& expr,
694
125k
                      const cel::Constant& const_expr) override {
695
125k
    if (!progress_status_.ok()) {
696
2.18k
      return;
697
2.18k
    }
698
699
123k
    absl::StatusOr<cel::Value> converted_value =
700
123k
        ConvertConstant(const_expr, cel::NewDeleteAllocator());
701
702
123k
    if (!converted_value.ok()) {
703
0
      SetProgressStatusIfError(converted_value.status());
704
0
      return;
705
0
    }
706
707
123k
    if (options_.max_recursion_depth > 0 || options_.max_recursion_depth < 0) {
708
0
      SetRecursiveStep(CreateConstValueDirectStep(
709
0
                           std::move(converted_value).value(), expr.id()),
710
0
                       1);
711
0
      return;
712
0
    }
713
714
123k
    AddStep(
715
123k
        CreateConstValueStep(std::move(converted_value).value(), expr.id()));
716
123k
  }
717
718
  struct SlotLookupResult {
719
    int slot;
720
    int subexpression;
721
  };
722
723
  // Helper to lookup a variable mapped to a slot.
724
  //
725
  // If lazy evaluation enabled and ided as a lazy expression,
726
  // subexpression and slot will be set.
727
54.3k
  SlotLookupResult LookupSlot(absl::string_view path) {
728
    // If there's a leading dot, it cannot resolve to a local variable.
729
54.3k
    if (absl::StartsWith(path, ".")) {
730
388
      return {-1, -1};
731
388
    }
732
53.9k
    if (block_.has_value()) {
733
0
      const BlockInfo& block = *block_;
734
0
      if (block.in) {
735
0
        absl::string_view index_suffix = path;
736
0
        if (absl::ConsumePrefix(&index_suffix, "@index")) {
737
0
          size_t index;
738
0
          if (!absl::SimpleAtoi(index_suffix, &index)) {
739
0
            SetProgressStatusIfError(
740
0
                issue_collector_.AddIssue(RuntimeIssue::CreateError(
741
0
                    absl::InvalidArgumentError("bad @index"))));
742
0
            return {-1, -1};
743
0
          }
744
0
          if (index >= block.size) {
745
0
            SetProgressStatusIfError(
746
0
                issue_collector_.AddIssue(RuntimeIssue::CreateError(
747
0
                    absl::InvalidArgumentError(absl::StrCat(
748
0
                        "invalid @index greater than number of bindings: ",
749
0
                        index, " >= ", block.size)))));
750
0
            return {-1, -1};
751
0
          }
752
0
          if (index >= block.current_index) {
753
0
            SetProgressStatusIfError(
754
0
                issue_collector_.AddIssue(RuntimeIssue::CreateError(
755
0
                    absl::InvalidArgumentError(absl::StrCat(
756
0
                        "@index references current or future binding: ", index,
757
0
                        " >= ", block.current_index)))));
758
0
            return {-1, -1};
759
0
          }
760
0
          return {static_cast<int>(block.index + index),
761
0
                  block.subexpressions[index]};
762
0
        }
763
0
      }
764
0
    }
765
53.9k
    if (!comprehension_stack_.empty()) {
766
45.0k
      for (int i = comprehension_stack_.size() - 1; i >= 0; i--) {
767
34.3k
        const ComprehensionStackRecord& record = comprehension_stack_[i];
768
34.3k
        if (record.iter_var_in_scope &&
769
22.6k
            record.comprehension->iter_var() == path) {
770
4.75k
          if (record.is_optimizable_bind) {
771
0
            SetProgressStatusIfError(issue_collector_.AddIssue(
772
0
                RuntimeIssue::CreateWarning(absl::InvalidArgumentError(
773
0
                    "Unexpected iter_var access in trivial comprehension"))));
774
0
            return {-1, -1};
775
0
          }
776
4.75k
          return {static_cast<int>(record.iter_slot), -1};
777
4.75k
        }
778
29.6k
        if (record.iter_var2_in_scope &&
779
17.8k
            record.comprehension->iter_var2() == path) {
780
0
          return {static_cast<int>(record.iter2_slot), -1};
781
0
        }
782
29.6k
        if (record.accu_var_in_scope &&
783
20.7k
            record.comprehension->accu_var() == path) {
784
5.87k
          int slot = record.accu_slot;
785
5.87k
          int subexpression = -1;
786
5.87k
          if (record.is_optimizable_bind) {
787
0
            subexpression = record.subexpression;
788
0
          }
789
5.87k
          return {slot, subexpression};
790
5.87k
        }
791
29.6k
      }
792
21.2k
    }
793
43.3k
    if (absl::StartsWith(path, "@it:") || absl::StartsWith(path, "@it2:") ||
794
43.3k
        absl::StartsWith(path, "@ac:")) {
795
      // If we see a CSE generated comprehension variable that was not
796
      // resolvable through the normal comprehension scope resolution, reject it
797
      // now rather than surfacing errors at activation time.
798
0
      SetProgressStatusIfError(
799
0
          issue_collector_.AddIssue(RuntimeIssue::CreateError(
800
0
              absl::InvalidArgumentError("out of scope reference to CSE "
801
0
                                         "generated comprehension variable"))));
802
0
    }
803
43.3k
    return {-1, -1};
804
53.9k
  }
805
806
  // Ident node handler.
807
  // Invoked after child nodes are processed.
808
  void PostVisitIdent(const cel::Expr& expr,
809
60.5k
                      const cel::IdentExpr& ident_expr) override {
810
60.5k
    if (!progress_status_.ok()) {
811
6.18k
      return;
812
6.18k
    }
813
54.3k
    absl::string_view path = ident_expr.name();
814
54.3k
    if (!ValidateOrError(
815
54.3k
            !path.empty(),
816
54.3k
            "Invalid expression: identifier 'name' must not be empty")) {
817
0
      return;
818
0
    }
819
820
    // Check if this is a local variable first (since it should shadow most
821
    // other interpretations).
822
54.3k
    SlotLookupResult slot = LookupSlot(path);
823
824
54.3k
    if (slot.subexpression >= 0) {
825
0
      auto* subexpression =
826
0
          program_builder_.GetExtractedSubexpression(slot.subexpression);
827
0
      if (subexpression == nullptr) {
828
0
        SetProgressStatusIfError(
829
0
            absl::InternalError("bad subexpression reference"));
830
0
        return;
831
0
      }
832
0
      if (subexpression->IsRecursive()) {
833
0
        const auto& program = subexpression->recursive_program();
834
0
        SetRecursiveStep(
835
0
            CreateDirectLazyInitStep(slot.slot, program.step.get(), expr.id()),
836
0
            program.depth + 1);
837
0
      } else {
838
        // Off by one since mainline expression will be index 0.
839
0
        AddStep(
840
0
            CreateLazyInitStep(slot.slot, slot.subexpression + 1, expr.id()));
841
0
      }
842
0
      return;
843
54.3k
    } else if (slot.slot >= 0) {
844
10.6k
      if (options_.max_recursion_depth != 0) {
845
0
        SetRecursiveStep(
846
0
            CreateDirectSlotIdentStep(ident_expr.name(), slot.slot, expr.id()),
847
0
            1);
848
10.6k
      } else {
849
10.6k
        AddStep(
850
10.6k
            CreateIdentStepForSlot(ident_expr.name(), slot.slot, expr.id()));
851
10.6k
      }
852
10.6k
      return;
853
10.6k
    }
854
855
    // Attempt to resolve a select expression as a namespaced identifier for an
856
    // enum or type constant value.
857
43.6k
    std::optional<cel::Value> const_value;
858
43.6k
    int64_t select_root_id = -1;
859
43.6k
    std::string path_candidate;
860
861
50.5k
    while (!namespace_stack_.empty()) {
862
6.87k
      const auto& select_node = namespace_stack_.front();
863
      // Generate path in format "<ident>.<field 0>.<field 1>...".
864
6.87k
      const cel::Expr* select_expr = select_node.first;
865
6.87k
      path_candidate = absl::StrCat(path, ".", select_node.second);
866
867
      // Attempt to find a constant enum or type value which matches the
868
      // qualified path present in the expression. Whether the identifier
869
      // can be resolved to a type instance depends on whether the option to
870
      // 'enable_qualified_type_identifiers' is set to true.
871
6.87k
      const_value = resolver_.FindConstant(path_candidate, select_expr->id());
872
6.87k
      if (const_value) {
873
47
        resolved_select_expr_ = select_expr;
874
47
        select_root_id = select_expr->id();
875
47
        path = path_candidate;
876
47
        namespace_stack_.clear();
877
47
        break;
878
47
      }
879
6.82k
      namespace_stack_.pop_front();
880
6.82k
    }
881
882
43.6k
    if (!const_value) {
883
      // Attempt to resolve a simple identifier as an enum or type constant
884
      // value.
885
43.6k
      const_value = resolver_.FindConstant(path, expr.id());
886
43.6k
      select_root_id = expr.id();
887
43.6k
    }
888
889
    // TODO(issues/97): Need to add support for resolving packaged names at
890
    // runtime if Parse-only. For checked, checker should have reported the
891
    // expected interpretation.
892
43.6k
    if (const_value) {
893
      // If the path starts with a dot, strip it.
894
480
      absl::string_view name = absl::StripPrefix(path, ".");
895
480
      if (options_.max_recursion_depth != 0) {
896
0
        SetRecursiveStep(
897
0
            CreateDirectShadowableValueStep(
898
0
                name, std::move(const_value).value(), select_root_id),
899
0
            1);
900
0
        return;
901
0
      }
902
480
      AddStep(CreateShadowableValueStep(name, std::move(const_value).value(),
903
480
                                        select_root_id));
904
480
      return;
905
480
    }
906
907
43.2k
    absl::string_view ident_name = absl::StripPrefix(ident_expr.name(), ".");
908
43.2k
    if (options_.max_recursion_depth != 0) {
909
0
      SetRecursiveStep(CreateDirectIdentStep(ident_name, expr.id()), 1);
910
43.2k
    } else {
911
43.2k
      AddStep(CreateIdentStep(ident_name, expr.id()));
912
43.2k
    }
913
43.2k
  }
914
915
  void PreVisitSelect(const cel::Expr& expr,
916
11.7k
                      const cel::SelectExpr& select_expr) override {
917
11.7k
    if (!progress_status_.ok()) {
918
222
      return;
919
222
    }
920
11.5k
    if (!ValidateOrError(
921
11.5k
            !select_expr.field().empty(),
922
11.5k
            "invalid expression: select 'field' must not be empty")) {
923
0
      return;
924
0
    }
925
11.5k
    if (!ValidateOrError(
926
11.5k
            select_expr.has_operand() &&
927
11.5k
                select_expr.operand().kind_case() !=
928
11.5k
                    cel::ExprKindCase::kUnspecifiedExpr,
929
11.5k
            "invalid expression: select must specify an operand")) {
930
0
      return;
931
0
    }
932
933
    // Not exactly the cleanest solution - we peek into child of
934
    // select_expr.
935
    // Chain of multiple SELECT ending with IDENT can represent namespaced
936
    // entity.
937
11.5k
    if (!select_expr.test_only() && (select_expr.operand().has_ident_expr() ||
938
10.1k
                                     select_expr.operand().has_select_expr())) {
939
      // select expressions are pushed in reverse order:
940
      // google.type.Expr is pushed as:
941
      // - field: 'Expr'
942
      // - field: 'type'
943
      // - id: 'google'
944
      //
945
      // The search order though is as follows:
946
      // - id: 'google.type.Expr'
947
      // - id: 'google.type', field: 'Expr'
948
      // - id: 'google', field: 'type', field: 'Expr'
949
163k
      for (size_t i = 0; i < namespace_stack_.size(); i++) {
950
154k
        auto ns = namespace_stack_[i];
951
154k
        namespace_stack_[i] = {
952
154k
            ns.first, absl::StrCat(select_expr.field(), ".", ns.second)};
953
154k
      }
954
8.67k
      namespace_stack_.push_back({&expr, select_expr.field()});
955
8.67k
    } else {
956
2.82k
      namespace_stack_.clear();
957
2.82k
    }
958
11.5k
  }
959
960
  // Select node handler.
961
  // Invoked after child nodes are processed.
962
  void PostVisitSelect(const cel::Expr& expr,
963
11.7k
                       const cel::SelectExpr& select_expr) override {
964
11.7k
    if (!progress_status_.ok()) {
965
409
      return;
966
409
    }
967
968
    // Check if we are "in the middle" of namespaced name.
969
    // This is currently enum specific. Constant expression that corresponds
970
    // to resolved enum value has been already created, thus preceding chain
971
    // of selects is no longer relevant.
972
11.3k
    if (resolved_select_expr_) {
973
141
      if (&expr == resolved_select_expr_) {
974
47
        resolved_select_expr_ = nullptr;
975
47
      }
976
141
      return;
977
141
    }
978
979
11.1k
    if (auto depth = RecursionEligible(); depth.has_value()) {
980
0
      auto deps = ExtractRecursiveDependencies();
981
0
      if (deps.size() != 1) {
982
0
        SetProgressStatusIfError(absl::InternalError(
983
0
            "unexpected number of dependencies for select operation."));
984
0
        return;
985
0
      }
986
0
      StringValue field = cel::StringValue(select_expr.field());
987
988
0
      SetRecursiveStep(
989
0
          CreateDirectSelectStep(std::move(deps[0]), std::move(field),
990
0
                                 select_expr.test_only(), expr.id(),
991
0
                                 options_.enable_empty_wrapper_null_unboxing,
992
0
                                 enable_optional_types_),
993
0
          *depth + 1);
994
0
      return;
995
0
    }
996
997
11.1k
    AddStep(CreateSelectStep(select_expr, expr.id(),
998
11.1k
                             options_.enable_empty_wrapper_null_unboxing,
999
11.1k
                             enable_optional_types_));
1000
11.1k
  }
1001
1002
  // Call node handler group.
1003
  // We provide finer granularity for Call node callbacks to allow special
1004
  // handling for short-circuiting
1005
  // PreVisitCall is invoked before child nodes are processed.
1006
  void PreVisitCall(const cel::Expr& expr,
1007
120k
                    const cel::CallExpr& call_expr) override {
1008
120k
    if (!progress_status_.ok()) {
1009
3.08k
      return;
1010
3.08k
    }
1011
1012
117k
    std::unique_ptr<CondVisitor> cond_visitor;
1013
117k
    if (call_expr.function() == cel::builtin::kAnd) {
1014
3.16k
      cond_visitor = std::make_unique<LogicalCondVisitor>(
1015
3.16k
          this, /*is_or=*/false, options_.short_circuiting);
1016
114k
    } else if (call_expr.function() == cel::builtin::kOr) {
1017
3.44k
      cond_visitor = std::make_unique<LogicalCondVisitor>(
1018
3.44k
          this, /*is_or=*/true, options_.short_circuiting);
1019
110k
    } else if (call_expr.function() == cel::builtin::kTernary) {
1020
572
      if (options_.short_circuiting) {
1021
572
        cond_visitor = std::make_unique<TernaryCondVisitor>(this);
1022
572
      } else {
1023
0
        cond_visitor = std::make_unique<ExhaustiveTernaryCondVisitor>(this);
1024
0
      }
1025
110k
    } else if (enable_optional_types_ &&
1026
0
               call_expr.function() == kOptionalOrFn &&
1027
0
               call_expr.has_target() && call_expr.args().size() == 1) {
1028
0
      cond_visitor = std::make_unique<OptionalOrCondVisitor>(
1029
0
          this, /*is_or_value=*/false, options_.short_circuiting);
1030
110k
    } else if (enable_optional_types_ &&
1031
0
               call_expr.function() == kOptionalOrValueFn &&
1032
0
               call_expr.has_target() && call_expr.args().size() == 1) {
1033
0
      cond_visitor = std::make_unique<OptionalOrCondVisitor>(
1034
0
          this, /*is_or_value=*/true, options_.short_circuiting);
1035
110k
    } else if (IsBlock(&call_expr)) {
1036
      // cel.@block
1037
0
      if (block_.has_value()) {
1038
        // There can only be one for now.
1039
0
        SetProgressStatusIfError(
1040
0
            absl::InvalidArgumentError("multiple cel.@block are not allowed"));
1041
0
        return;
1042
0
      }
1043
0
      block_ = BlockInfo();
1044
0
      BlockInfo& block = *block_;
1045
0
      block.in = true;
1046
0
      if (call_expr.args().empty()) {
1047
0
        SetProgressStatusIfError(absl::InvalidArgumentError(
1048
0
            "malformed cel.@block: missing list of bound expressions"));
1049
0
        return;
1050
0
      }
1051
0
      if (call_expr.args().size() != 2) {
1052
0
        SetProgressStatusIfError(absl::InvalidArgumentError(
1053
0
            "malformed cel.@block: missing bound expression"));
1054
0
        return;
1055
0
      }
1056
0
      if (!call_expr.args()[0].has_list_expr()) {
1057
0
        SetProgressStatusIfError(
1058
0
            absl::InvalidArgumentError("malformed cel.@block: first argument "
1059
0
                                       "is not a list of bound expressions"));
1060
0
        return;
1061
0
      }
1062
0
      const auto& list_expr = call_expr.args().front().list_expr();
1063
0
      block.size = list_expr.elements().size();
1064
1065
0
      block.bindings_set.reserve(block.size);
1066
0
      for (const auto& list_expr_element : list_expr.elements()) {
1067
0
        if (list_expr_element.optional()) {
1068
0
          SetProgressStatusIfError(
1069
0
              absl::InvalidArgumentError("malformed cel.@block: list of bound "
1070
0
                                         "expressions contains an optional"));
1071
0
          return;
1072
0
        }
1073
0
        block.bindings_set.insert(&list_expr_element.expr());
1074
0
      }
1075
0
      block.index = index_manager().ReserveSlots(block.size);
1076
0
      block.slot_count = block.size;
1077
0
      block.expr = &expr;
1078
0
      block.bindings = &call_expr.args()[0];
1079
0
      block.bound = &call_expr.args()[1];
1080
0
      block.subexpressions.resize(block.size, -1);
1081
110k
    } else {
1082
110k
      return;
1083
110k
    }
1084
1085
7.17k
    if (cond_visitor) {
1086
7.17k
      cond_visitor->PreVisit(&expr);
1087
7.17k
      cond_visitor_stack_.push({&expr, std::move(cond_visitor)});
1088
7.17k
    }
1089
7.17k
  }
1090
1091
  // Returns the maximum recursion depth of the current program if it is
1092
  // eligible for recursion, or nullopt if it is not.
1093
143k
  std::optional<int> RecursionEligible() {
1094
143k
    if (!PlanRecursiveProgram() || program_builder_.current() == nullptr) {
1095
143k
      return std::nullopt;
1096
143k
    }
1097
0
    return program_builder_.current()->RecursiveDependencyDepth();
1098
143k
  }
1099
1100
  std::vector<std::unique_ptr<DirectExpressionStep>>
1101
0
  ExtractRecursiveDependencies() {
1102
    // Must check recursion eligibility before calling.
1103
0
    ABSL_DCHECK(program_builder_.current() != nullptr);
1104
1105
0
    return program_builder_.current()->ExtractRecursiveDependencies();
1106
0
  }
1107
1108
0
  void MakeTernaryRecursive(const cel::Expr* expr) {
1109
0
    if (expr->call_expr().args().size() != 3) {
1110
0
      SetProgressStatusIfError(absl::InvalidArgumentError(
1111
0
          "unexpected number of args for builtin ternary"));
1112
0
      return;
1113
0
    }
1114
1115
0
    const cel::Expr* condition_expr = &expr->call_expr().args()[0];
1116
0
    const cel::Expr* left_expr = &expr->call_expr().args()[1];
1117
0
    const cel::Expr* right_expr = &expr->call_expr().args()[2];
1118
1119
0
    auto* condition_plan = program_builder_.GetSubexpression(condition_expr);
1120
0
    auto* left_plan = program_builder_.GetSubexpression(left_expr);
1121
0
    auto* right_plan = program_builder_.GetSubexpression(right_expr);
1122
1123
0
    if (condition_plan == nullptr || !condition_plan->IsRecursive() ||
1124
0
        left_plan == nullptr || !left_plan->IsRecursive() ||
1125
0
        right_plan == nullptr || !right_plan->IsRecursive()) {
1126
0
      SetProgressStatusIfError(FailedRecursivePlanning());
1127
0
      return;
1128
0
    }
1129
1130
0
    int max_depth = std::max({0, condition_plan->recursive_program().depth,
1131
0
                              left_plan->recursive_program().depth,
1132
0
                              right_plan->recursive_program().depth});
1133
1134
0
    SetRecursiveStep(
1135
0
        CreateDirectTernaryStep(condition_plan->ExtractRecursiveProgram().step,
1136
0
                                left_plan->ExtractRecursiveProgram().step,
1137
0
                                right_plan->ExtractRecursiveProgram().step,
1138
0
                                expr->id(), options_.short_circuiting),
1139
0
        max_depth + 1);
1140
0
  }
1141
1142
0
  void MakeShortcircuitRecursive(const cel::Expr* expr, bool is_or) {
1143
0
    int args_size = expr->call_expr().args().size();
1144
0
    if (args_size < 2) {
1145
0
      SetProgressStatusIfError(absl::InvalidArgumentError(
1146
0
          "unexpected number of args for builtin boolean operator &&/||"));
1147
0
      return;
1148
0
    }
1149
1150
0
    auto* current_plan =
1151
0
        program_builder_.GetSubexpression(&expr->call_expr().args()[0]);
1152
0
    if (current_plan == nullptr || !current_plan->IsRecursive()) {
1153
0
      SetProgressStatusIfError(FailedRecursivePlanning());
1154
0
      return;
1155
0
    }
1156
0
    int current_depth = current_plan->recursive_program().depth;
1157
0
    std::unique_ptr<DirectExpressionStep> current_step =
1158
0
        current_plan->ExtractRecursiveProgram().step;
1159
1160
0
    for (int i = 1; i < args_size; ++i) {
1161
0
      auto* next_plan =
1162
0
          program_builder_.GetSubexpression(&expr->call_expr().args()[i]);
1163
0
      if (next_plan == nullptr || !next_plan->IsRecursive()) {
1164
0
        SetProgressStatusIfError(FailedRecursivePlanning());
1165
0
        return;
1166
0
      }
1167
0
      current_depth =
1168
0
          std::max(current_depth, next_plan->recursive_program().depth);
1169
0
      std::unique_ptr<DirectExpressionStep> next_step =
1170
0
          next_plan->ExtractRecursiveProgram().step;
1171
0
      if (is_or) {
1172
0
        current_step =
1173
0
            CreateDirectOrStep(std::move(current_step), std::move(next_step),
1174
0
                               expr->id(), options_.short_circuiting);
1175
0
      } else {
1176
0
        current_step =
1177
0
            CreateDirectAndStep(std::move(current_step), std::move(next_step),
1178
0
                                expr->id(), options_.short_circuiting);
1179
0
      }
1180
0
      current_depth++;
1181
0
    }
1182
0
    SetRecursiveStep(std::move(current_step), current_depth);
1183
0
  }
1184
1185
0
  void MakeOptionalShortcircuit(const cel::Expr* expr, bool is_or_value) {
1186
0
    if (!expr->call_expr().has_target() ||
1187
0
        expr->call_expr().args().size() != 1) {
1188
0
      SetProgressStatusIfError(absl::InvalidArgumentError(
1189
0
          "unexpected number of args for optional.or{Value}"));
1190
0
      return;
1191
0
    }
1192
0
    const cel::Expr* left_expr = &expr->call_expr().target();
1193
0
    const cel::Expr* right_expr = &expr->call_expr().args()[0];
1194
1195
0
    auto* left_plan = program_builder_.GetSubexpression(left_expr);
1196
0
    auto* right_plan = program_builder_.GetSubexpression(right_expr);
1197
1198
0
    if (left_plan == nullptr || !left_plan->IsRecursive() ||
1199
0
        right_plan == nullptr || !right_plan->IsRecursive()) {
1200
0
      SetProgressStatusIfError(FailedRecursivePlanning());
1201
0
      return;
1202
0
    }
1203
0
    int max_depth = std::max({0, left_plan->recursive_program().depth,
1204
0
                              right_plan->recursive_program().depth});
1205
1206
0
    SetRecursiveStep(CreateDirectOptionalOrStep(
1207
0
                         expr->id(), left_plan->ExtractRecursiveProgram().step,
1208
0
                         right_plan->ExtractRecursiveProgram().step,
1209
0
                         is_or_value, options_.short_circuiting),
1210
0
                     max_depth + 1);
1211
0
  }
1212
1213
  void MaybeMakeBindRecursive(const cel::Expr* expr,
1214
                              const cel::ComprehensionExpr* comprehension,
1215
0
                              size_t accu_slot) {
1216
0
    if (!PlanRecursiveProgram()) {
1217
0
      return;
1218
0
    }
1219
1220
0
    auto* result_plan =
1221
0
        program_builder_.GetSubexpression(&comprehension->result());
1222
1223
0
    if (result_plan == nullptr || !result_plan->IsRecursive()) {
1224
0
      SetProgressStatusIfError(FailedRecursivePlanning());
1225
0
      return;
1226
0
    }
1227
1228
0
    int result_depth = result_plan->recursive_program().depth;
1229
1230
0
    auto program = result_plan->ExtractRecursiveProgram();
1231
0
    SetRecursiveStep(
1232
0
        CreateDirectBindStep(accu_slot, std::move(program.step), expr->id()),
1233
0
        result_depth + 1);
1234
0
  }
1235
1236
  void MaybeMakeComprehensionRecursive(
1237
      const cel::Expr* expr, const cel::ComprehensionExpr* comprehension,
1238
2.85k
      size_t iter_slot, size_t iter2_slot, size_t accu_slot) {
1239
2.85k
    if (!PlanRecursiveProgram()) {
1240
2.85k
      return;
1241
2.85k
    }
1242
1243
0
    auto* accu_plan =
1244
0
        program_builder_.GetSubexpression(&comprehension->accu_init());
1245
0
    auto* range_plan =
1246
0
        program_builder_.GetSubexpression(&comprehension->iter_range());
1247
0
    auto* loop_plan =
1248
0
        program_builder_.GetSubexpression(&comprehension->loop_step());
1249
0
    auto* condition_plan =
1250
0
        program_builder_.GetSubexpression(&comprehension->loop_condition());
1251
0
    auto* result_plan =
1252
0
        program_builder_.GetSubexpression(&comprehension->result());
1253
0
    if (accu_plan == nullptr || !accu_plan->IsRecursive() ||
1254
0
        range_plan == nullptr || !range_plan->IsRecursive() ||
1255
0
        loop_plan == nullptr || !loop_plan->IsRecursive() ||
1256
0
        condition_plan == nullptr || !condition_plan->IsRecursive() ||
1257
0
        result_plan == nullptr || !result_plan->IsRecursive()) {
1258
0
      SetProgressStatusIfError(FailedRecursivePlanning());
1259
0
      return;
1260
0
    }
1261
1262
0
    int max_depth = 0;
1263
0
    max_depth = std::max(max_depth, accu_plan->recursive_program().depth);
1264
0
    max_depth = std::max(max_depth, range_plan->recursive_program().depth);
1265
0
    max_depth = std::max(max_depth, loop_plan->recursive_program().depth);
1266
0
    max_depth = std::max(max_depth, condition_plan->recursive_program().depth);
1267
0
    max_depth = std::max(max_depth, result_plan->recursive_program().depth);
1268
1269
0
    auto step = CreateDirectComprehensionStep(
1270
0
        iter_slot, iter2_slot, accu_slot,
1271
0
        range_plan->ExtractRecursiveProgram().step,
1272
0
        accu_plan->ExtractRecursiveProgram().step,
1273
0
        loop_plan->ExtractRecursiveProgram().step,
1274
0
        condition_plan->ExtractRecursiveProgram().step,
1275
0
        result_plan->ExtractRecursiveProgram().step, options_.short_circuiting,
1276
0
        expr->id());
1277
1278
0
    SetRecursiveStep(std::move(step), max_depth + 1);
1279
0
  }
1280
1281
  // Invoked after all child nodes are processed.
1282
  void PostVisitCall(const cel::Expr& expr,
1283
120k
                     const cel::CallExpr& call_expr) override {
1284
120k
    if (!progress_status_.ok()) {
1285
4.81k
      return;
1286
4.81k
    }
1287
1288
115k
    auto cond_visitor = FindCondVisitor(&expr);
1289
115k
    if (cond_visitor) {
1290
7.02k
      cond_visitor->PostVisit(&expr);
1291
7.02k
      cond_visitor_stack_.pop();
1292
7.02k
      return;
1293
7.02k
    }
1294
1295
    // Check if the call is intercepted by a custom handler.
1296
108k
    if (auto handler = call_handlers_.find(call_expr.function());
1297
108k
        handler != call_handlers_.end()) {
1298
23.2k
      CallHandlerResult result = handler->second(expr, call_expr);
1299
23.2k
      if (result == CallHandlerResult::kIntercepted) {
1300
14.0k
        return;
1301
14.0k
      }  // otherwise, apply default function handling.
1302
23.2k
    }
1303
1304
94.3k
    AddResolvedFunctionStep(&call_expr, &expr, call_expr.function());
1305
94.3k
  }
1306
1307
  void PreVisitComprehension(
1308
      const cel::Expr& expr,
1309
4.52k
      const cel::ComprehensionExpr& comprehension) override {
1310
4.52k
    if (!progress_status_.ok()) {
1311
415
      return;
1312
415
    }
1313
4.11k
    if (!ValidateOrError(options_.enable_comprehension,
1314
4.11k
                         "Comprehension support is disabled")) {
1315
0
      return;
1316
0
    }
1317
4.11k
    const auto& accu_var = comprehension.accu_var();
1318
4.11k
    const auto& iter_var = comprehension.iter_var();
1319
4.11k
    const auto& iter_var2 = comprehension.iter_var2();
1320
4.11k
    ValidateOrError(!accu_var.empty(),
1321
4.11k
                    "Invalid comprehension: 'accu_var' must not be empty");
1322
4.11k
    ValidateOrError(!iter_var.empty(),
1323
4.11k
                    "Invalid comprehension: 'iter_var' must not be empty");
1324
4.11k
    ValidateOrError(
1325
4.11k
        accu_var != iter_var,
1326
4.11k
        "Invalid comprehension: 'accu_var' must not be the same as 'iter_var'");
1327
4.11k
    ValidateOrError(accu_var != iter_var2,
1328
4.11k
                    "Invalid comprehension: 'accu_var' must not be the same as "
1329
4.11k
                    "'iter_var2'");
1330
4.11k
    ValidateOrError(iter_var2 != iter_var,
1331
4.11k
                    "Invalid comprehension: 'iter_var2' must not be the same "
1332
4.11k
                    "as 'iter_var'");
1333
4.11k
    ValidateOrError(comprehension.has_accu_init(),
1334
4.11k
                    "Invalid comprehension: 'accu_init' must be set");
1335
4.11k
    ValidateOrError(comprehension.has_loop_condition(),
1336
4.11k
                    "Invalid comprehension: 'loop_condition' must be set");
1337
4.11k
    ValidateOrError(comprehension.has_loop_step(),
1338
4.11k
                    "Invalid comprehension: 'loop_step' must be set");
1339
4.11k
    ValidateOrError(comprehension.has_result(),
1340
4.11k
                    "Invalid comprehension: 'result' must be set");
1341
1342
4.11k
    size_t iter_slot, iter2_slot, accu_slot, slot_count;
1343
4.11k
    bool is_bind = IsBind(&comprehension);
1344
1345
4.11k
    if (is_bind) {
1346
0
      accu_slot = iter_slot = iter2_slot = index_manager_.ReserveSlots(1);
1347
0
      slot_count = 1;
1348
4.11k
    } else if (comprehension.iter_var2().empty()) {
1349
4.11k
      iter_slot = iter2_slot = index_manager_.ReserveSlots(2);
1350
4.11k
      accu_slot = iter_slot + 1;
1351
4.11k
      slot_count = 2;
1352
4.11k
    } else {
1353
0
      iter_slot = index_manager_.ReserveSlots(3);
1354
0
      iter2_slot = iter_slot + 1;
1355
0
      accu_slot = iter2_slot + 1;
1356
0
      slot_count = 3;
1357
0
    }
1358
1359
4.11k
    if (block_.has_value()) {
1360
0
      BlockInfo& block = *block_;
1361
0
      if (block.in) {
1362
0
        block.slot_count += slot_count;
1363
0
        slot_count = 0;
1364
0
      }
1365
0
    }
1366
    // If this is in the scope of an optimized bind accu-init, account the slots
1367
    // to the outermost bind-init scope.
1368
    //
1369
    // The init expression is effectively inlined at the first usage in the
1370
    // critical path (which is unknown at plan time), so the used slots need to
1371
    // be dedicated for the entire scope of that bind.
1372
39.4k
    for (ComprehensionStackRecord& record : comprehension_stack_) {
1373
39.4k
      if (record.in_accu_init && record.is_optimizable_bind) {
1374
0
        record.slot_count += slot_count;
1375
0
        slot_count = 0;
1376
0
        break;
1377
0
      }
1378
      // If no bind init subexpression, account normally.
1379
39.4k
    }
1380
1381
4.11k
    comprehension_stack_.push_back(
1382
4.11k
        {&expr, &comprehension, iter_slot, iter2_slot, accu_slot, slot_count,
1383
4.11k
         /*subexpression=*/-1,
1384
         /*.is_optimizable_list_append=*/
1385
4.11k
         IsOptimizableListAppend(&comprehension,
1386
4.11k
                                 options_.enable_comprehension_list_append),
1387
         /*.is_optimizable_map_insert=*/
1388
4.11k
         IsOptimizableMapInsert(&comprehension,
1389
4.11k
                                options_.enable_comprehension_mutable_map),
1390
4.11k
         /*.is_optimizable_bind=*/is_bind,
1391
4.11k
         /*.iter_var_in_scope=*/false,
1392
4.11k
         /*.iter_var2_in_scope=*/false,
1393
4.11k
         /*.accu_var_in_scope=*/false,
1394
4.11k
         /*.in_accu_init=*/false,
1395
4.11k
         std::make_unique<ComprehensionVisitor>(this, options_.short_circuiting,
1396
4.11k
                                                is_bind, iter_slot, iter2_slot,
1397
4.11k
                                                accu_slot)});
1398
4.11k
    comprehension_stack_.back().visitor->PreVisit(&expr);
1399
4.11k
  }
1400
1401
  // Invoked after all child nodes are processed.
1402
  void PostVisitComprehension(
1403
      const cel::Expr& expr,
1404
4.52k
      const cel::ComprehensionExpr& comprehension_expr) override {
1405
4.52k
    if (!progress_status_.ok()) {
1406
1.67k
      return;
1407
1.67k
    }
1408
1409
2.85k
    ComprehensionStackRecord& record = comprehension_stack_.back();
1410
2.85k
    if (comprehension_stack_.empty() ||
1411
2.85k
        record.comprehension != &comprehension_expr) {
1412
0
      return;
1413
0
    }
1414
1415
2.85k
    record.visitor->PostVisit(&expr);
1416
1417
2.85k
    index_manager_.ReleaseSlots(record.slot_count);
1418
2.85k
    comprehension_stack_.pop_back();
1419
2.85k
  }
1420
1421
  void PreVisitComprehensionSubexpression(
1422
      const cel::Expr& expr, const cel::ComprehensionExpr& compr,
1423
22.6k
      cel::ComprehensionArg comprehension_arg) override {
1424
22.6k
    if (!progress_status_.ok()) {
1425
7.05k
      return;
1426
7.05k
    }
1427
1428
15.5k
    if (comprehension_stack_.empty() ||
1429
15.5k
        comprehension_stack_.back().comprehension != &compr) {
1430
0
      return;
1431
0
    }
1432
1433
15.5k
    ComprehensionStackRecord& record = comprehension_stack_.back();
1434
1435
15.5k
    switch (comprehension_arg) {
1436
4.11k
      case cel::ITER_RANGE: {
1437
4.11k
        record.in_accu_init = false;
1438
4.11k
        record.iter_var_in_scope = false;
1439
4.11k
        record.iter_var2_in_scope = false;
1440
4.11k
        record.accu_var_in_scope = false;
1441
4.11k
        break;
1442
0
      }
1443
2.87k
      case cel::ACCU_INIT: {
1444
2.87k
        record.in_accu_init = true;
1445
2.87k
        record.iter_var_in_scope = false;
1446
2.87k
        record.iter_var2_in_scope = false;
1447
2.87k
        record.accu_var_in_scope = false;
1448
2.87k
        break;
1449
0
      }
1450
2.87k
      case cel::LOOP_CONDITION: {
1451
2.87k
        record.in_accu_init = false;
1452
2.87k
        record.iter_var_in_scope = true;
1453
2.87k
        record.iter_var2_in_scope = true;
1454
2.87k
        record.accu_var_in_scope = true;
1455
2.87k
        break;
1456
0
      }
1457
2.87k
      case cel::LOOP_STEP: {
1458
2.87k
        record.in_accu_init = false;
1459
2.87k
        record.iter_var_in_scope = true;
1460
2.87k
        record.iter_var2_in_scope = true;
1461
2.87k
        record.accu_var_in_scope = true;
1462
2.87k
        break;
1463
0
      }
1464
2.85k
      case cel::RESULT: {
1465
2.85k
        record.in_accu_init = false;
1466
2.85k
        record.iter_var_in_scope = false;
1467
2.85k
        record.iter_var2_in_scope = false;
1468
2.85k
        record.accu_var_in_scope = true;
1469
2.85k
        break;
1470
0
      }
1471
15.5k
    }
1472
15.5k
  }
1473
1474
  void PostVisitComprehensionSubexpression(
1475
      const cel::Expr& expr, const cel::ComprehensionExpr& compr,
1476
22.6k
      cel::ComprehensionArg comprehension_arg) override {
1477
22.6k
    if (!progress_status_.ok()) {
1478
8.31k
      return;
1479
8.31k
    }
1480
1481
14.3k
    if (comprehension_stack_.empty() ||
1482
14.3k
        comprehension_stack_.back().comprehension != &compr) {
1483
0
      return;
1484
0
    }
1485
1486
14.3k
    SetProgressStatusIfError(comprehension_stack_.back().visitor->PostVisitArg(
1487
14.3k
        comprehension_arg, comprehension_stack_.back().expr));
1488
14.3k
  }
1489
1490
  // Invoked after each argument node processed.
1491
226k
  void PostVisitArg(const cel::Expr& expr, int arg_num) override {
1492
226k
    if (!progress_status_.ok()) {
1493
7.98k
      return;
1494
7.98k
    }
1495
218k
    auto cond_visitor = FindCondVisitor(&expr);
1496
218k
    if (cond_visitor) {
1497
14.7k
      cond_visitor->PostVisitArg(arg_num, &expr);
1498
14.7k
    }
1499
218k
  }
1500
1501
962
  void PostVisitTarget(const cel::Expr& expr) override {
1502
962
    if (!progress_status_.ok()) {
1503
748
      return;
1504
748
    }
1505
214
    auto cond_visitor = FindCondVisitor(&expr);
1506
214
    if (cond_visitor) {
1507
0
      cond_visitor->PostVisitTarget(&expr);
1508
0
    }
1509
214
  }
1510
1511
  // CreateList node handler.
1512
  // Invoked after child nodes are processed.
1513
  void PostVisitList(const cel::Expr& expr,
1514
20.2k
                     const cel::ListExpr& list_expr) override {
1515
20.2k
    if (!progress_status_.ok()) {
1516
3.56k
      return;
1517
3.56k
    }
1518
1519
16.7k
    if (block_.has_value()) {
1520
0
      BlockInfo& block = *block_;
1521
0
      if (block.bindings == &expr) {
1522
        // Do nothing, this is the cel.@block bindings list.
1523
0
        return;
1524
0
      }
1525
0
    }
1526
1527
16.7k
    if (!comprehension_stack_.empty()) {
1528
8.55k
      const ComprehensionStackRecord& comprehension =
1529
8.55k
          comprehension_stack_.back();
1530
8.55k
      if (comprehension.is_optimizable_list_append) {
1531
0
        if (&(comprehension.comprehension->accu_init()) == &expr) {
1532
0
          if (PlanRecursiveProgram()) {
1533
0
            SetRecursiveStep(CreateDirectMutableListStep(expr.id()), 1);
1534
0
            return;
1535
0
          }
1536
0
          AddStep(CreateMutableListStep(expr.id()));
1537
0
          return;
1538
0
        }
1539
0
        if (GetOptimizableListAppendOperand(comprehension.comprehension) ==
1540
0
            &expr) {
1541
0
          return;
1542
0
        }
1543
0
      }
1544
8.55k
    }
1545
16.7k
    if (std::optional<int> depth = RecursionEligible(); depth.has_value()) {
1546
0
      auto deps = ExtractRecursiveDependencies();
1547
0
      if (deps.size() != list_expr.elements().size()) {
1548
0
        SetProgressStatusIfError(absl::InternalError(
1549
0
            "Unexpected number of plan elements for CreateList expr"));
1550
0
        return;
1551
0
      }
1552
0
      auto step = CreateDirectListStep(
1553
0
          std::move(deps), MakeOptionalIndicesSet(list_expr), expr.id());
1554
0
      SetRecursiveStep(std::move(step), *depth + 1);
1555
0
      return;
1556
0
    }
1557
16.7k
    AddStep(CreateCreateListStep(list_expr, expr.id()));
1558
16.7k
  }
1559
1560
  // CreateStruct node handler.
1561
  // Invoked after child nodes are processed.
1562
  void PostVisitStruct(const cel::Expr& expr,
1563
1.90k
                       const cel::StructExpr& struct_expr) override {
1564
1.90k
    if (!progress_status_.ok()) {
1565
197
      return;
1566
197
    }
1567
1568
1.70k
    auto status_or_resolved_fields =
1569
1.70k
        ResolveCreateStructFields(struct_expr, expr.id());
1570
1.70k
    if (!status_or_resolved_fields.ok()) {
1571
223
      SetProgressStatusIfError(status_or_resolved_fields.status());
1572
223
      return;
1573
223
    }
1574
1.48k
    std::string resolved_name =
1575
1.48k
        std::move(status_or_resolved_fields.value().first);
1576
1.48k
    std::vector<std::string> fields =
1577
1.48k
        std::move(status_or_resolved_fields.value().second);
1578
1579
1.48k
    if (auto depth = RecursionEligible(); depth.has_value()) {
1580
0
      auto deps = ExtractRecursiveDependencies();
1581
0
      if (deps.size() != struct_expr.fields().size()) {
1582
0
        SetProgressStatusIfError(absl::InternalError(
1583
0
            "Unexpected number of plan elements for CreateStruct expr"));
1584
0
        return;
1585
0
      }
1586
0
      auto step = CreateDirectCreateStructStep(
1587
0
          std::move(resolved_name), std::move(fields), std::move(deps),
1588
0
          MakeOptionalIndicesSet(struct_expr), expr.id());
1589
0
      SetRecursiveStep(std::move(step), *depth + 1);
1590
0
      return;
1591
0
    }
1592
1593
1.48k
    AddStep(CreateCreateStructStep(std::move(resolved_name), std::move(fields),
1594
1.48k
                                   MakeOptionalIndicesSet(struct_expr),
1595
1.48k
                                   expr.id()));
1596
1.48k
  }
1597
1598
  void PostVisitMap(const cel::Expr& expr,
1599
6.03k
                    const cel::MapExpr& map_expr) override {
1600
6.03k
    for (const auto& entry : map_expr.entries()) {
1601
4.51k
      ValidateOrError(entry.has_key(), "Map entry missing key");
1602
4.51k
      ValidateOrError(entry.has_value(), "Map entry missing value");
1603
4.51k
    }
1604
1605
6.03k
    if (!comprehension_stack_.empty()) {
1606
1.09k
      const ComprehensionStackRecord& comprehension =
1607
1.09k
          comprehension_stack_.back();
1608
1.09k
      if (comprehension.is_optimizable_map_insert) {
1609
0
        if (&(comprehension.comprehension->accu_init()) == &expr) {
1610
0
          if (PlanRecursiveProgram()) {
1611
0
            SetRecursiveStep(CreateDirectMutableMapStep(expr.id()), 1);
1612
0
            return;
1613
0
          }
1614
0
          AddStep(CreateMutableMapStep(expr.id()));
1615
0
          return;
1616
0
        }
1617
0
      }
1618
1.09k
    }
1619
1620
6.03k
    if (auto depth = RecursionEligible(); depth.has_value()) {
1621
0
      auto deps = ExtractRecursiveDependencies();
1622
0
      if (deps.size() != 2 * map_expr.entries().size()) {
1623
0
        SetProgressStatusIfError(absl::InternalError(
1624
0
            "Unexpected number of plan elements for CreateStruct expr"));
1625
0
        return;
1626
0
      }
1627
0
      auto step = CreateDirectCreateMapStep(
1628
0
          std::move(deps), MakeOptionalIndicesSet(map_expr), expr.id());
1629
0
      SetRecursiveStep(std::move(step), *depth + 1);
1630
0
      return;
1631
0
    }
1632
6.03k
    AddStep(CreateCreateStructStepForMap(map_expr.entries().size(),
1633
6.03k
                                         MakeOptionalIndicesSet(map_expr),
1634
6.03k
                                         expr.id()));
1635
6.03k
  }
1636
1637
11.2k
  absl::Status progress_status() const { return progress_status_; }
1638
1639
  // Mark a branch as suppressed. The visitor will continue as normal, but
1640
  // any emitted program steps are ignored.
1641
  //
1642
  // Only applies to branches that have not yet been visited (pre-order).
1643
0
  void SuppressBranch(const cel::Expr* expr) {
1644
0
    suppressed_branches_.insert(expr);
1645
0
  }
1646
1647
  void AddResolvedFunctionStep(const cel::CallExpr* call_expr,
1648
                               const cel::Expr* expr,
1649
94.3k
                               absl::string_view function) {
1650
    // Establish the search criteria for a given function.
1651
94.3k
    bool receiver_style = call_expr->has_target();
1652
94.3k
    size_t num_args = call_expr->args().size() + (receiver_style ? 1 : 0);
1653
1654
    // First, search for lazily defined function overloads.
1655
    // Lazy functions shadow eager functions with the same signature.
1656
94.3k
    auto lazy_overloads = resolver_.FindLazyOverloads(
1657
94.3k
        function, call_expr->has_target(), num_args, expr->id());
1658
94.3k
    if (!lazy_overloads.empty()) {
1659
0
      if (auto depth = RecursionEligible(); depth.has_value()) {
1660
0
        auto args = program_builder_.current()->ExtractRecursiveDependencies();
1661
0
        SetRecursiveStep(CreateDirectLazyFunctionStep(
1662
0
                             expr->id(), *call_expr, std::move(args),
1663
0
                             std::move(lazy_overloads)),
1664
0
                         *depth + 1);
1665
0
        return;
1666
0
      }
1667
0
      AddStep(CreateFunctionStep(*call_expr, expr->id(),
1668
0
                                 std::move(lazy_overloads)));
1669
0
      return;
1670
0
    }
1671
1672
    // Second, search for eagerly defined function overloads.
1673
94.3k
    auto overloads =
1674
94.3k
        resolver_.FindOverloads(function, receiver_style, num_args, expr->id());
1675
94.3k
    if (overloads.empty()) {
1676
      // Create a warning that the overload could not be found. Depending on the
1677
      // builder_warnings configuration, this could result in termination of the
1678
      // CelExpression creation or an inspectable warning for use within runtime
1679
      // logging.
1680
179
      auto status = issue_collector_.AddIssue(RuntimeIssue::CreateWarning(
1681
179
          absl::InvalidArgumentError(
1682
179
              "No overloads provided for FunctionStep creation"),
1683
179
          RuntimeIssue::ErrorCode::kNoMatchingOverload));
1684
179
      if (!status.ok()) {
1685
179
        SetProgressStatusIfError(status);
1686
179
        return;
1687
179
      }
1688
179
    }
1689
1690
94.2k
    if (auto recursion_depth = RecursionEligible();
1691
94.2k
        recursion_depth.has_value()) {
1692
      // Nonnull while active -- nullptr indicates logic error elsewhere in the
1693
      // builder.
1694
0
      ABSL_DCHECK(program_builder_.current() != nullptr);
1695
0
      auto args = program_builder_.current()->ExtractRecursiveDependencies();
1696
0
      SetRecursiveStep(
1697
0
          CreateDirectFunctionStep(expr->id(), *call_expr, std::move(args),
1698
0
                                   std::move(overloads)),
1699
0
          *recursion_depth + 1);
1700
0
      return;
1701
0
    }
1702
94.2k
    AddStep(CreateFunctionStep(*call_expr, expr->id(), std::move(overloads)));
1703
94.2k
  }
1704
1705
  // Add a step to the program, taking ownership. If successful, returns the
1706
  // pointer to the step. Otherwise, returns nullptr.
1707
  //
1708
  // Note: the pointer is only guaranteed to stay valid until the parent
1709
  // subexpression is finalized. Optimizers may modify the program plan which
1710
  // may free the step at that point.
1711
  ExpressionStep* AddStep(
1712
316k
      absl::StatusOr<std::unique_ptr<ExpressionStep>> step) {
1713
316k
    if (step.ok()) {
1714
316k
      return AddStep(*std::move(step));
1715
316k
    } else {
1716
0
      SetProgressStatusIfError(step.status());
1717
0
    }
1718
0
    return nullptr;
1719
316k
  }
1720
1721
  template <typename T>
1722
  std::enable_if_t<std::is_base_of_v<ExpressionStep, T>, T*> AddStep(
1723
350k
      std::unique_ptr<T> step) {
1724
350k
    if (progress_status_.ok() && !PlanningSuppressed()) {
1725
350k
      return static_cast<T*>(program_builder_.AddStep(std::move(step)));
1726
350k
    }
1727
361
    return nullptr;
1728
350k
  }
flat_expr_builder.cc:_ZN6google3api4expr7runtime12_GLOBAL__N_115FlatExprVisitor7AddStepINS2_14ExpressionStepEEENSt3__19enable_ifIXsr3stdE12is_base_of_vIS6_T_EEPS9_E4typeENS7_10unique_ptrIS9_NS7_14default_deleteIS9_EEEE
Line
Count
Source
1723
330k
      std::unique_ptr<T> step) {
1724
330k
    if (progress_status_.ok() && !PlanningSuppressed()) {
1725
330k
      return static_cast<T*>(program_builder_.AddStep(std::move(step)));
1726
330k
    }
1727
361
    return nullptr;
1728
330k
  }
flat_expr_builder.cc:_ZN6google3api4expr7runtime12_GLOBAL__N_115FlatExprVisitor7AddStepINS2_12JumpStepBaseEEENSt3__19enable_ifIXsr3stdE12is_base_of_vINS2_14ExpressionStepET_EEPSA_E4typeENS7_10unique_ptrISA_NS7_14default_deleteISA_EEEE
Line
Count
Source
1723
11.1k
      std::unique_ptr<T> step) {
1724
11.1k
    if (progress_status_.ok() && !PlanningSuppressed()) {
1725
11.1k
      return static_cast<T*>(program_builder_.AddStep(std::move(step)));
1726
11.1k
    }
1727
0
    return nullptr;
1728
11.1k
  }
flat_expr_builder.cc:_ZN6google3api4expr7runtime12_GLOBAL__N_115FlatExprVisitor7AddStepINS2_21ComprehensionInitStepEEENSt3__19enable_ifIXsr3stdE12is_base_of_vINS2_14ExpressionStepET_EEPSA_E4typeENS7_10unique_ptrISA_NS7_14default_deleteISA_EEEE
Line
Count
Source
1723
2.87k
      std::unique_ptr<T> step) {
1724
2.87k
    if (progress_status_.ok() && !PlanningSuppressed()) {
1725
2.87k
      return static_cast<T*>(program_builder_.AddStep(std::move(step)));
1726
2.87k
    }
1727
0
    return nullptr;
1728
2.87k
  }
flat_expr_builder.cc:_ZN6google3api4expr7runtime12_GLOBAL__N_115FlatExprVisitor7AddStepINS2_21ComprehensionNextStepEEENSt3__19enable_ifIXsr3stdE12is_base_of_vINS2_14ExpressionStepET_EEPSA_E4typeENS7_10unique_ptrISA_NS7_14default_deleteISA_EEEE
Line
Count
Source
1723
2.87k
      std::unique_ptr<T> step) {
1724
2.87k
    if (progress_status_.ok() && !PlanningSuppressed()) {
1725
2.87k
      return static_cast<T*>(program_builder_.AddStep(std::move(step)));
1726
2.87k
    }
1727
0
    return nullptr;
1728
2.87k
  }
flat_expr_builder.cc:_ZN6google3api4expr7runtime12_GLOBAL__N_115FlatExprVisitor7AddStepINS2_21ComprehensionCondStepEEENSt3__19enable_ifIXsr3stdE12is_base_of_vINS2_14ExpressionStepET_EEPSA_E4typeENS7_10unique_ptrISA_NS7_14default_deleteISA_EEEE
Line
Count
Source
1723
2.87k
      std::unique_ptr<T> step) {
1724
2.87k
    if (progress_status_.ok() && !PlanningSuppressed()) {
1725
2.87k
      return static_cast<T*>(program_builder_.AddStep(std::move(step)));
1726
2.87k
    }
1727
0
    return nullptr;
1728
2.87k
  }
1729
1730
0
  void SetRecursiveStep(std::unique_ptr<DirectExpressionStep> step, int depth) {
1731
0
    if (!progress_status_.ok() || PlanningSuppressed()) {
1732
0
      return;
1733
0
    }
1734
0
    if (program_builder_.current() == nullptr) {
1735
0
      SetProgressStatusIfError(absl::InternalError(
1736
0
          "CEL AST traversal out of order in flat_expr_builder."));
1737
0
      return;
1738
0
    }
1739
0
    program_builder_.current()->set_recursive_program(std::move(step), depth);
1740
0
    if (depth > max_recursion_depth_) {
1741
0
      SetProgressStatusIfError(absl::InvalidArgumentError(
1742
0
          absl::StrCat("Maximum recursion depth of ",
1743
0
                       options_.max_recursion_depth, " exceeded")));
1744
0
    }
1745
0
  }
1746
1747
25.6k
  void SetProgressStatusIfError(const absl::Status& status) {
1748
25.6k
    if (progress_status_.ok() && !status.ok()) {
1749
411
      progress_status_ = status;
1750
411
    }
1751
25.6k
  }
1752
1753
  // Index of the next step to be inserted, in terms of the current
1754
  // subexpression
1755
42.1k
  ProgramStepIndex GetCurrentIndex() const {
1756
    // Nonnull while active -- nullptr indicates logic error in the builder.
1757
42.1k
    ABSL_DCHECK(program_builder_.current() != nullptr);
1758
42.1k
    return {static_cast<int>(program_builder_.current()->elements().size()),
1759
42.1k
            program_builder_.current()};
1760
42.1k
  }
1761
1762
334k
  CondVisitor* FindCondVisitor(const cel::Expr* expr) const {
1763
334k
    if (cond_visitor_stack_.empty()) {
1764
198k
      return nullptr;
1765
198k
    }
1766
1767
135k
    const auto& latest = cond_visitor_stack_.top();
1768
1769
135k
    return (latest.first == expr) ? latest.second.get() : nullptr;
1770
334k
  }
1771
1772
0
  IndexManager& index_manager() { return index_manager_; }
1773
1774
10.3k
  size_t slot_count() const { return index_manager_.max_slot_count(); }
1775
1776
0
  void AddOptimizer(std::unique_ptr<ProgramOptimizer> optimizer) {
1777
0
    program_optimizers_.push_back(std::move(optimizer));
1778
0
  }
1779
1780
  // Tests the boolean predicate, and if false produces an InvalidArgumentError
1781
  // which concatenates the error_message and any optional message_parts as the
1782
  // error status message.
1783
  template <typename... MP>
1784
  bool ValidateOrError(bool valid_expression, absl::string_view error_message,
1785
501k
                       MP... message_parts) {
1786
501k
    if (valid_expression) {
1787
501k
      return true;
1788
501k
    }
1789
9
    SetProgressStatusIfError(absl::InvalidArgumentError(
1790
9
        absl::StrCat(error_message, message_parts...)));
1791
9
    return false;
1792
501k
  }
1793
1794
 private:
1795
  struct ComprehensionStackRecord {
1796
    const cel::Expr* expr;
1797
    const cel::ComprehensionExpr* comprehension;
1798
    size_t iter_slot;
1799
    size_t iter2_slot;
1800
    size_t accu_slot;
1801
    size_t slot_count;
1802
    // -1 indicates this shouldn't be used.
1803
    int subexpression;
1804
    bool is_optimizable_list_append;
1805
    bool is_optimizable_map_insert;
1806
    bool is_optimizable_bind;
1807
    bool iter_var_in_scope;
1808
    bool iter_var2_in_scope;
1809
    bool accu_var_in_scope;
1810
    bool in_accu_init;
1811
    std::unique_ptr<ComprehensionVisitor> visitor;
1812
  };
1813
1814
  struct BlockInfo {
1815
    // True if we are currently visiting the `cel.@block` node or any of its
1816
    // children.
1817
    bool in = false;
1818
    // Pointer to the `cel.@block` node.
1819
    const cel::Expr* expr = nullptr;
1820
    // Pointer to the `cel.@block` bindings, that is the first argument to the
1821
    // function.
1822
    const cel::Expr* bindings = nullptr;
1823
    // Set of pointers to the elements of `bindings` above.
1824
    absl::flat_hash_set<const cel::Expr*> bindings_set;
1825
    // Pointer to the `cel.@block` bound expression, that is the second argument
1826
    // to the function.
1827
    const cel::Expr* bound = nullptr;
1828
    // The number of entries in the `cel.@block`.
1829
    size_t size = 0;
1830
    // Starting slot index for `cel.@block`. We occupy he slot indices `index`
1831
    // through `index + size + (var_size * 2)`.
1832
    size_t index = 0;
1833
    // The total number of slots needed for evaluating the bound expressions.
1834
    size_t slot_count = 0;
1835
    // The current slot index we are processing, any index references must be
1836
    // less than this to be valid.
1837
    size_t current_index = 0;
1838
    // Pointer to the current `cel.@block` being processed, that is one of the
1839
    // elements within the first argument.
1840
    const cel::Expr* current_binding = nullptr;
1841
    // Mapping between block indices and their subexpressions, fixed size with
1842
    // exactly `size` elements. Unprocessed indices are set to `-1`.
1843
    std::vector<int> subexpressions;
1844
  };
1845
1846
350k
  bool PlanningSuppressed() const {
1847
350k
    return resume_from_suppressed_branch_ != nullptr;
1848
350k
  }
1849
1850
  absl::Status MaybeExtractSubexpression(const cel::Expr* expr,
1851
0
                                         ComprehensionStackRecord& record) {
1852
0
    if (!record.is_optimizable_bind) {
1853
0
      return absl::OkStatus();
1854
0
    }
1855
1856
0
    int index = program_builder_.ExtractSubexpression(expr);
1857
0
    if (index == -1) {
1858
0
      return absl::InternalError("Failed to extract subexpression");
1859
0
    }
1860
1861
0
    record.subexpression = index;
1862
1863
0
    record.visitor->MarkAccuInitExtracted();
1864
1865
0
    return absl::OkStatus();
1866
0
  }
1867
1868
  // Resolve the name of the message type being created and the names of set
1869
  // fields.
1870
  absl::StatusOr<std::pair<std::string, std::vector<std::string>>>
1871
  ResolveCreateStructFields(const cel::StructExpr& create_struct_expr,
1872
1.70k
                            int64_t expr_id) {
1873
1.70k
    absl::string_view ast_name = create_struct_expr.name();
1874
1875
1.70k
    std::optional<std::pair<std::string, cel::Type>> type;
1876
1.70k
    CEL_ASSIGN_OR_RETURN(type, resolver_.FindType(ast_name, expr_id));
1877
1878
1.70k
    if (!type.has_value()) {
1879
188
      return absl::InvalidArgumentError(absl::StrCat(
1880
188
          "Invalid struct creation: missing type info for '", ast_name, "'"));
1881
188
    }
1882
1883
1.52k
    std::string resolved_name = std::move(type).value().first;
1884
1885
1.52k
    std::vector<std::string> fields;
1886
1.52k
    fields.reserve(create_struct_expr.fields().size());
1887
1.52k
    for (const auto& entry : create_struct_expr.fields()) {
1888
35
      if (entry.name().empty()) {
1889
0
        return absl::InvalidArgumentError("Struct field missing name");
1890
0
      }
1891
35
      if (!entry.has_value()) {
1892
0
        return absl::InvalidArgumentError("Struct field missing value");
1893
0
      }
1894
70
      CEL_ASSIGN_OR_RETURN(auto field, type_provider_.FindStructTypeFieldByName(
1895
70
                                           resolved_name, entry.name()));
1896
70
      if (!field.has_value()) {
1897
35
        return absl::InvalidArgumentError(
1898
35
            absl::StrCat("Invalid message creation: field '", entry.name(),
1899
35
                         "' not found in '", resolved_name, "'"));
1900
35
      }
1901
0
      fields.push_back(entry.name());
1902
0
    }
1903
1904
1.48k
    return std::make_pair(std::move(resolved_name), std::move(fields));
1905
1.52k
  }
1906
1907
  CallHandlerResult HandleIndex(const cel::Expr& expr,
1908
                                const cel::CallExpr& call);
1909
  CallHandlerResult HandleBlock(const cel::Expr& expr,
1910
                                const cel::CallExpr& call);
1911
  CallHandlerResult HandleListAppend(const cel::Expr& expr,
1912
                                     const cel::CallExpr& call);
1913
  CallHandlerResult HandleNot(const cel::Expr& expr, const cel::CallExpr& call);
1914
  CallHandlerResult HandleNotStrictlyFalse(const cel::Expr& expr,
1915
                                           const cel::CallExpr& call);
1916
1917
  CallHandlerResult HandleHeterogeneousEquality(const cel::Expr& expr,
1918
                                                const cel::CallExpr& call,
1919
                                                bool inequality);
1920
1921
  CallHandlerResult HandleHeterogeneousEqualityIn(const cel::Expr& expr,
1922
                                                  const cel::CallExpr& call);
1923
1924
  const Resolver& resolver_;
1925
  const cel::TypeProvider& type_provider_;
1926
  absl::Status progress_status_;
1927
  absl::flat_hash_map<std::string, CallHandler> call_handlers_;
1928
1929
  std::stack<std::pair<const cel::Expr*, std::unique_ptr<CondVisitor>>>
1930
      cond_visitor_stack_;
1931
1932
  // Tracks SELECT-...SELECT-IDENT chains.
1933
  std::deque<std::pair<const cel::Expr*, std::string>> namespace_stack_;
1934
1935
  // When multiple SELECT-...SELECT-IDENT chain is resolved as namespace, this
1936
  // field is used as marker suppressing CelExpression creation for SELECTs.
1937
  const cel::Expr* resolved_select_expr_;
1938
1939
  const cel::RuntimeOptions& options_;
1940
1941
  std::vector<ComprehensionStackRecord> comprehension_stack_;
1942
  absl::flat_hash_set<const cel::Expr*> suppressed_branches_;
1943
  const cel::Expr* resume_from_suppressed_branch_ = nullptr;
1944
  std::vector<std::unique_ptr<ProgramOptimizer>> program_optimizers_;
1945
  IssueCollector& issue_collector_;
1946
1947
  ProgramBuilder& program_builder_;
1948
  PlannerContext& extension_context_;
1949
  IndexManager index_manager_;
1950
1951
  bool enable_optional_types_;
1952
  std::optional<FlatExprVisitor::BlockInfo> block_;
1953
  int max_recursion_depth_ = 0;
1954
};
1955
1956
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleIndex(
1957
3.94k
    const cel::Expr& expr, const cel::CallExpr& call_expr) {
1958
3.94k
  ABSL_DCHECK(call_expr.function() == cel::builtin::kIndex);
1959
3.94k
  if (!ValidateOrError(
1960
3.94k
          (call_expr.args().size() == 2 && !call_expr.has_target()) ||
1961
              // TODO(uncreated-issue/79): A few clients use the index operator with a
1962
              // target in custom ASTs.
1963
0
              (call_expr.args().size() == 1 && call_expr.has_target()),
1964
3.94k
          "unexpected number of args for builtin index operator")) {
1965
0
    return CallHandlerResult::kIntercepted;
1966
0
  }
1967
1968
3.94k
  if (auto depth = RecursionEligible(); depth.has_value()) {
1969
0
    auto args = ExtractRecursiveDependencies();
1970
0
    if (args.size() != 2) {
1971
0
      SetProgressStatusIfError(absl::InvalidArgumentError(
1972
0
          "unexpected number of args for builtin index operator"));
1973
0
      return CallHandlerResult::kIntercepted;
1974
0
    }
1975
0
    SetRecursiveStep(
1976
0
        CreateDirectContainerAccessStep(std::move(args[0]), std::move(args[1]),
1977
0
                                        enable_optional_types_, expr.id()),
1978
0
        *depth + 1);
1979
0
    return CallHandlerResult::kIntercepted;
1980
0
  }
1981
3.94k
  AddStep(
1982
3.94k
      CreateContainerAccessStep(call_expr, expr.id(), enable_optional_types_));
1983
3.94k
  return CallHandlerResult::kIntercepted;
1984
3.94k
}
1985
1986
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleNot(
1987
1.02k
    const cel::Expr& expr, const cel::CallExpr& call_expr) {
1988
1.02k
  ABSL_DCHECK(call_expr.function() == cel::builtin::kNot);
1989
1990
1.02k
  if (!ValidateOrError(call_expr.args().size() == 1 && !call_expr.has_target(),
1991
1.02k
                       "unexpected number of args for builtin not operator")) {
1992
0
    return CallHandlerResult::kIntercepted;
1993
0
  }
1994
1995
1.02k
  if (auto depth = RecursionEligible(); depth.has_value()) {
1996
0
    auto args = ExtractRecursiveDependencies();
1997
0
    if (args.size() != 1) {
1998
0
      SetProgressStatusIfError(absl::InvalidArgumentError(
1999
0
          "unexpected number of args for builtin not operator"));
2000
0
      return CallHandlerResult::kIntercepted;
2001
0
    }
2002
0
    SetRecursiveStep(CreateDirectNotStep(std::move(args[0]), expr.id()),
2003
0
                     *depth + 1);
2004
0
    return CallHandlerResult::kIntercepted;
2005
0
  }
2006
1.02k
  AddStep(CreateNotStep(expr.id()));
2007
1.02k
  return CallHandlerResult::kIntercepted;
2008
1.02k
}
2009
2010
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleNotStrictlyFalse(
2011
0
    const cel::Expr& expr, const cel::CallExpr& call_expr) {
2012
0
  if (!ValidateOrError(call_expr.args().size() == 1 && !call_expr.has_target(),
2013
0
                       "unexpected number of args for builtin "
2014
0
                       "not_strictly_false operator")) {
2015
0
    return CallHandlerResult::kIntercepted;
2016
0
  }
2017
2018
0
  if (auto depth = RecursionEligible(); depth.has_value()) {
2019
0
    auto args = ExtractRecursiveDependencies();
2020
0
    if (args.size() != 1) {
2021
0
      SetProgressStatusIfError(
2022
0
          absl::InvalidArgumentError("unexpected number of args for builtin "
2023
0
                                     "@not_strictly_false operator"));
2024
0
      return CallHandlerResult::kIntercepted;
2025
0
    }
2026
0
    SetRecursiveStep(
2027
0
        CreateDirectNotStrictlyFalseStep(std::move(args[0]), expr.id()),
2028
0
        *depth + 1);
2029
0
    return CallHandlerResult::kIntercepted;
2030
0
  }
2031
0
  AddStep(CreateNotStrictlyFalseStep(expr.id()));
2032
0
  return CallHandlerResult::kIntercepted;
2033
0
}
2034
2035
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleBlock(
2036
0
    const cel::Expr& expr, const cel::CallExpr& call_expr) {
2037
0
  ABSL_DCHECK(call_expr.function() == kBlock);
2038
0
  if (!block_.has_value() || block_->expr != &expr ||
2039
0
      call_expr.args().size() != 2 || call_expr.has_target()) {
2040
0
    SetProgressStatusIfError(
2041
0
        absl::InvalidArgumentError("unexpected call to internal cel.@block"));
2042
0
    return CallHandlerResult::kIntercepted;
2043
0
  }
2044
2045
0
  BlockInfo& block = *block_;
2046
0
  block.in = false;
2047
0
  index_manager().ReleaseSlots(block.slot_count);
2048
2049
  // Check if eligible for recursion and update the plan if so.
2050
  //
2051
  // The first argument to @block is the list of initializers. These don't
2052
  // generate a plan in the main program (they are tracked separately to support
2053
  // lazy evaluation) so we only need to extract the second argument -- the body
2054
  // of the block that uses the initializers.
2055
0
  ProgramBuilder::Subexpression* body_subexpression =
2056
0
      program_builder_.GetSubexpression(&call_expr.args()[1]);
2057
2058
0
  if (options_.max_recursion_depth != 0 && body_subexpression != nullptr &&
2059
0
      body_subexpression->IsRecursive() &&
2060
0
      (options_.max_recursion_depth < 0 ||
2061
0
       body_subexpression->recursive_program().depth <
2062
0
           options_.max_recursion_depth)) {
2063
0
    auto recursive_program = body_subexpression->ExtractRecursiveProgram();
2064
0
    SetRecursiveStep(
2065
0
        CreateDirectBlockStep(block.index, block.slot_count,
2066
0
                              std::move(recursive_program.step), expr.id()),
2067
0
        recursive_program.depth + 1);
2068
0
    return CallHandlerResult::kIntercepted;
2069
0
  }
2070
2071
  // Otherwise, iterative plan.
2072
0
  if (block.slot_count > 0) {
2073
0
    AddStep(CreateClearSlotsStep(block.index, block.slot_count, expr.id()));
2074
0
  }
2075
2076
0
  return CallHandlerResult::kIntercepted;
2077
0
}
2078
2079
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleListAppend(
2080
9.22k
    const cel::Expr& expr, const cel::CallExpr& call_expr) {
2081
9.22k
  ABSL_DCHECK(call_expr.function() == cel::builtin::kAdd);
2082
2083
  // Check to see if this is a special case of add that should really be
2084
  // treated as a list append
2085
9.22k
  if (!comprehension_stack_.empty() &&
2086
3.91k
      comprehension_stack_.back().is_optimizable_list_append) {
2087
    // Already checked that this is an optimizeable comprehension,
2088
    // check that this is the correct list append node.
2089
0
    const cel::ComprehensionExpr* comprehension =
2090
0
        comprehension_stack_.back().comprehension;
2091
0
    const cel::Expr& loop_step = comprehension->loop_step();
2092
    // Macro loop_step for a map() will contain a list concat operation:
2093
    //   accu_var + [elem]
2094
0
    if (&loop_step == &expr) {
2095
0
      AddResolvedFunctionStep(&call_expr, &expr,
2096
0
                              cel::builtin::kRuntimeListAppend);
2097
0
      return CallHandlerResult::kIntercepted;
2098
0
    }
2099
    // Macro loop_step for a filter() will contain a ternary:
2100
    //   filter ? accu_var + [elem] : accu_var
2101
0
    if (loop_step.has_call_expr() &&
2102
0
        loop_step.call_expr().function() == cel::builtin::kTernary &&
2103
0
        loop_step.call_expr().args().size() == 3 &&
2104
0
        &(loop_step.call_expr().args()[1]) == &expr) {
2105
0
      AddResolvedFunctionStep(&call_expr, &expr,
2106
0
                              cel::builtin::kRuntimeListAppend);
2107
0
      return CallHandlerResult::kIntercepted;
2108
0
    }
2109
0
  }
2110
2111
9.22k
  return CallHandlerResult::kNotIntercepted;
2112
9.22k
}
2113
2114
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleHeterogeneousEquality(
2115
6.20k
    const cel::Expr& expr, const cel::CallExpr& call, bool inequality) {
2116
6.20k
  if (!ValidateOrError(
2117
6.20k
          call.args().size() == 2 && !call.has_target(),
2118
6.20k
          "unexpected number of args for builtin equality operator")) {
2119
0
    return CallHandlerResult::kIntercepted;
2120
0
  }
2121
2122
6.20k
  if (auto depth = RecursionEligible(); depth.has_value()) {
2123
0
    auto args = ExtractRecursiveDependencies();
2124
0
    if (args.size() != 2) {
2125
0
      SetProgressStatusIfError(absl::InvalidArgumentError(
2126
0
          "unexpected number of args for builtin equality operator"));
2127
0
      return CallHandlerResult::kIntercepted;
2128
0
    }
2129
0
    SetRecursiveStep(
2130
0
        CreateDirectEqualityStep(std::move(args[0]), std::move(args[1]),
2131
0
                                 inequality, expr.id()),
2132
0
        *depth + 1);
2133
0
    return CallHandlerResult::kIntercepted;
2134
0
  }
2135
6.20k
  AddStep(CreateEqualityStep(inequality, expr.id()));
2136
6.20k
  return CallHandlerResult::kIntercepted;
2137
6.20k
}
2138
2139
FlatExprVisitor::CallHandlerResult
2140
FlatExprVisitor::HandleHeterogeneousEqualityIn(const cel::Expr& expr,
2141
2.88k
                                               const cel::CallExpr& call) {
2142
2.88k
  if (!ValidateOrError(call.args().size() == 2 && !call.has_target(),
2143
2.88k
                       "unexpected number of args for builtin 'in' operator")) {
2144
9
    return CallHandlerResult::kIntercepted;
2145
9
  }
2146
2147
2.88k
  if (auto depth = RecursionEligible(); depth.has_value()) {
2148
0
    auto args = ExtractRecursiveDependencies();
2149
0
    if (args.size() != 2) {
2150
0
      SetProgressStatusIfError(absl::InvalidArgumentError(
2151
0
          "unexpected number of args for builtin 'in' operator"));
2152
0
      return CallHandlerResult::kIntercepted;
2153
0
    }
2154
0
    SetRecursiveStep(
2155
0
        CreateDirectInStep(std::move(args[0]), std::move(args[1]), expr.id()),
2156
0
        *depth + 1);
2157
0
    return CallHandlerResult::kIntercepted;
2158
0
  }
2159
2160
2.88k
  AddStep(CreateInStep(expr.id()));
2161
2.88k
  return CallHandlerResult::kIntercepted;
2162
2.88k
}
2163
2164
6.60k
void LogicalCondVisitor::PreVisit(const cel::Expr* expr) {
2165
6.60k
  visitor_->ValidateOrError(
2166
6.60k
      !expr->call_expr().has_target() && expr->call_expr().args().size() >= 2,
2167
6.60k
      "Invalid argument count for a binary function call.");
2168
6.60k
}
2169
2170
13.1k
void LogicalCondVisitor::PostVisitArg(int arg_num, const cel::Expr* expr) {
2171
13.1k
  if (visitor_->PlanRecursiveProgram()) {
2172
0
    return;
2173
0
  }
2174
13.1k
  const int last_arg_index = expr->call_expr().args().size() - 1;
2175
13.1k
  const size_t num_args = expr->call_expr().args().size();
2176
13.1k
  if (arg_num == last_arg_index) {
2177
6.55k
    if (is_or_) {
2178
3.41k
      visitor_->AddStep(CreateOrStep(num_args, expr->id()));
2179
3.41k
    } else {
2180
3.14k
      visitor_->AddStep(CreateAndStep(num_args, expr->id()));
2181
3.14k
    }
2182
6.55k
    if (short_circuiting_ && !jump_steps_.empty()) {
2183
6.55k
      for (auto& jump : jump_steps_) {
2184
6.55k
        visitor_->SetProgressStatusIfError(
2185
6.55k
            jump.set_target(visitor_->GetCurrentIndex()));
2186
6.55k
      }
2187
6.55k
    }
2188
6.55k
  }
2189
13.1k
  if (short_circuiting_ && arg_num < last_arg_index) {
2190
6.57k
    std::unique_ptr<JumpStepBase> jump_step =
2191
6.57k
        is_or_
2192
6.57k
            ? CreateCondJumpStep(true, {}, /*expected_stack_size=*/arg_num + 1,
2193
3.42k
                                 expr->id())
2194
6.57k
            : CreateCondJumpStep(false, {}, /*expected_stack_size=*/arg_num + 1,
2195
3.15k
                                 expr->id());
2196
6.57k
    ProgramStepIndex index = visitor_->GetCurrentIndex();
2197
6.57k
    if (JumpStepBase* jump_step_ptr = visitor_->AddStep(std::move(jump_step));
2198
6.57k
        jump_step_ptr) {
2199
6.57k
      jump_steps_.push_back(Jump(index, jump_step_ptr));
2200
6.57k
    }
2201
6.57k
  }
2202
13.1k
}
2203
2204
6.55k
void LogicalCondVisitor::PostVisit(const cel::Expr* expr) {
2205
6.55k
  if (visitor_->PlanRecursiveProgram()) {
2206
0
    visitor_->MakeShortcircuitRecursive(expr, is_or_);
2207
0
  }
2208
6.55k
}
2209
2210
0
void OptionalOrCondVisitor::PreVisit(const cel::Expr* expr) {
2211
0
  visitor_->ValidateOrError(
2212
0
      expr->call_expr().has_target() && expr->call_expr().args().size() == 1,
2213
0
      "Invalid argument count for or/orValue call.");
2214
0
}
2215
2216
0
void OptionalOrCondVisitor::PostVisitTarget(const cel::Expr* expr) {
2217
0
  if (visitor_->PlanRecursiveProgram()) {
2218
0
    return;
2219
0
  }
2220
0
  if (short_circuiting_) {
2221
    // If first branch evaluation result is enough to determine output,
2222
    // jump over the second branch and provide result of the first argument as
2223
    // final output.
2224
    // Retain a pointer to the jump step so we can update the target after
2225
    // planning the second argument.
2226
0
    std::unique_ptr<JumpStepBase> jump_step =
2227
0
        CreateOptionalHasValueJumpStep(is_or_value_, expr->id());
2228
0
    ProgramStepIndex index = visitor_->GetCurrentIndex();
2229
0
    if (JumpStepBase* jump_step_ptr = visitor_->AddStep(std::move(jump_step));
2230
0
        jump_step_ptr) {
2231
0
      jump_steps_.push_back(Jump(index, jump_step_ptr));
2232
0
    }
2233
0
  }
2234
0
}
2235
2236
0
void OptionalOrCondVisitor::PostVisit(const cel::Expr* expr) {
2237
0
  if (visitor_->PlanRecursiveProgram()) {
2238
0
    visitor_->MakeOptionalShortcircuit(expr, is_or_value_);
2239
0
    return;
2240
0
  }
2241
2242
0
  visitor_->AddStep(CreateOptionalOrStep(is_or_value_, expr->id()));
2243
0
  if (short_circuiting_) {
2244
0
    for (auto& jump : jump_steps_) {
2245
0
      visitor_->SetProgressStatusIfError(
2246
0
          jump.set_target(visitor_->GetCurrentIndex()));
2247
0
    }
2248
0
  }
2249
0
}
2250
2251
572
void TernaryCondVisitor::PreVisit(const cel::Expr* expr) {
2252
572
  visitor_->ValidateOrError(
2253
572
      !expr->call_expr().has_target() && expr->call_expr().args().size() == 3,
2254
572
      "Invalid argument count for a ternary function call.");
2255
572
}
2256
2257
1.60k
void TernaryCondVisitor::PostVisitArg(int arg_num, const cel::Expr* expr) {
2258
1.60k
  if (visitor_->PlanRecursiveProgram()) {
2259
0
    return;
2260
0
  }
2261
  // Ternary operator "_?_:_" requires a special handing.
2262
  // In contrary to regular function call, its execution affects the control
2263
  // flow of the overall CEL expression.
2264
  // If condition value (argument 0) is True, then control flow is unaffected
2265
  // as it is passed to the first conditional branch. Then, at the end of this
2266
  // branch, the jump is performed over the second conditional branch.
2267
  // If condition value is False, then jump is performed and control is passed
2268
  // to the beginning of the second conditional branch.
2269
  // If condition value is Error, then jump is peformed to bypass both
2270
  // conditional branches and provide Error as result of ternary operation.
2271
2272
  // condition argument for ternary operator
2273
1.60k
  if (arg_num == 0) {
2274
    // Jump in case of error or non-bool
2275
569
    ProgramStepIndex error_jump_pos = visitor_->GetCurrentIndex();
2276
569
    auto* error_jump =
2277
569
        visitor_->AddStep(CreateBoolCheckJumpStep({}, expr->id()));
2278
569
    if (error_jump) {
2279
569
      error_jump_ = Jump(error_jump_pos, error_jump);
2280
569
    }
2281
2282
    // Jump to the second branch of execution
2283
    // Value is to be removed from the stack.
2284
569
    ProgramStepIndex cond_jump_pos = visitor_->GetCurrentIndex();
2285
569
    auto* jump_to_second =
2286
569
        visitor_->AddStep(CreateTernaryCondJumpStep({}, expr->id()));
2287
569
    if (jump_to_second) {
2288
569
      jump_to_second_ =
2289
569
          Jump(cond_jump_pos, static_cast<JumpStepBase*>(jump_to_second));
2290
569
    }
2291
1.03k
  } else if (arg_num == 1) {
2292
    // Jump after the first and over the second branch of execution.
2293
    // Value is to be removed from the stack.
2294
565
    ProgramStepIndex jump_pos = visitor_->GetCurrentIndex();
2295
565
    auto* jump_after_first = visitor_->AddStep(CreateJumpStep({}, expr->id()));
2296
565
    if (!jump_after_first) {
2297
0
      return;
2298
0
    }
2299
565
    jump_after_first_ = Jump(jump_pos, jump_after_first);
2300
2301
565
    if (visitor_->ValidateOrError(
2302
565
            jump_to_second_.exists(),
2303
565
            "Error configuring ternary operator: jump_to_second_ is null")) {
2304
565
      visitor_->SetProgressStatusIfError(
2305
565
          jump_to_second_.set_target(visitor_->GetCurrentIndex()));
2306
565
    }
2307
565
  }
2308
  // Code executed after traversing the final branch of execution
2309
  // (arg_num == 2) is placed in PostVisitCall, to make this method less
2310
  // clattered.
2311
1.60k
}
2312
2313
474
void TernaryCondVisitor::PostVisit(const cel::Expr* expr) {
2314
474
  if (visitor_->PlanRecursiveProgram()) {
2315
0
    visitor_->MakeTernaryRecursive(expr);
2316
0
    return;
2317
0
  }
2318
  // Determine and set jump offset in jump instruction.
2319
474
  if (visitor_->ValidateOrError(
2320
474
          error_jump_.exists(),
2321
474
          "Error configuring ternary operator: error_jump_ is null")) {
2322
474
    visitor_->SetProgressStatusIfError(
2323
474
        error_jump_.set_target(visitor_->GetCurrentIndex()));
2324
474
  }
2325
474
  if (visitor_->ValidateOrError(
2326
474
          jump_after_first_.exists(),
2327
474
          "Error configuring ternary operator: jump_after_first_ is null")) {
2328
474
    visitor_->SetProgressStatusIfError(
2329
474
        jump_after_first_.set_target(visitor_->GetCurrentIndex()));
2330
474
  }
2331
474
}
2332
2333
0
void ExhaustiveTernaryCondVisitor::PreVisit(const cel::Expr* expr) {
2334
0
  visitor_->ValidateOrError(
2335
0
      !expr->call_expr().has_target() && expr->call_expr().args().size() == 3,
2336
0
      "Invalid argument count for a ternary function call.");
2337
0
}
2338
2339
0
void ExhaustiveTernaryCondVisitor::PostVisit(const cel::Expr* expr) {
2340
0
  if (visitor_->PlanRecursiveProgram()) {
2341
0
    visitor_->MakeTernaryRecursive(expr);
2342
0
    return;
2343
0
  }
2344
0
  visitor_->AddStep(CreateTernaryStep(expr->id()));
2345
0
}
2346
2347
4.11k
void ComprehensionVisitor::PreVisit(const cel::Expr* expr) {
2348
4.11k
  if (is_trivial_) {
2349
0
    visitor_->SuppressBranch(&expr->comprehension_expr().iter_range());
2350
0
    visitor_->SuppressBranch(&expr->comprehension_expr().loop_condition());
2351
0
    visitor_->SuppressBranch(&expr->comprehension_expr().loop_step());
2352
0
  }
2353
4.11k
}
2354
2355
absl::Status ComprehensionVisitor::PostVisitArgDefault(
2356
14.3k
    cel::ComprehensionArg arg_num, const cel::Expr* expr) {
2357
14.3k
  if (visitor_->PlanRecursiveProgram()) {
2358
0
    return absl::OkStatus();
2359
0
  }
2360
14.3k
  switch (arg_num) {
2361
2.87k
    case cel::ITER_RANGE: {
2362
2.87k
      init_step_pos_ = visitor_->GetCurrentIndex();
2363
2.87k
      init_step_ = visitor_->AddStep(
2364
2.87k
          std::make_unique<ComprehensionInitStep>(expr->id()));
2365
2.87k
      break;
2366
0
    }
2367
2.87k
    case cel::ACCU_INIT: {
2368
2.87k
      next_step_pos_ = visitor_->GetCurrentIndex();
2369
2.87k
      next_step_ = visitor_->AddStep(std::make_unique<ComprehensionNextStep>(
2370
2.87k
          iter_slot_, iter2_slot_, accu_slot_, expr->id()));
2371
2.87k
      break;
2372
0
    }
2373
2.87k
    case cel::LOOP_CONDITION: {
2374
2.87k
      cond_step_pos_ = visitor_->GetCurrentIndex();
2375
2.87k
      cond_step_ = visitor_->AddStep(std::make_unique<ComprehensionCondStep>(
2376
2.87k
          iter_slot_, iter2_slot_, accu_slot_, short_circuiting_, expr->id()));
2377
2.87k
      break;
2378
0
    }
2379
2.85k
    case cel::LOOP_STEP: {
2380
2.85k
      ProgramStepIndex index = visitor_->GetCurrentIndex();
2381
2.85k
      auto* jump_to_next = visitor_->AddStep(CreateJumpStep({}, expr->id()));
2382
2.85k
      if (!jump_to_next) {
2383
0
        break;
2384
0
      }
2385
2.85k
      Jump jump_helper(index, jump_to_next);
2386
2.85k
      visitor_->SetProgressStatusIfError(
2387
2.85k
          jump_helper.set_target(next_step_pos_));
2388
2389
      // Set offsets jumping to the result step.
2390
2.85k
      if (cond_step_) {
2391
2.85k
        CEL_ASSIGN_OR_RETURN(
2392
2.85k
            int jump_from_cond,
2393
2.85k
            Jump::CalculateOffset(cond_step_pos_, visitor_->GetCurrentIndex()));
2394
2.85k
        cond_step_->set_jump_offset(jump_from_cond);
2395
2.85k
      }
2396
2397
2.85k
      if (next_step_) {
2398
2.85k
        CEL_ASSIGN_OR_RETURN(
2399
2.85k
            int jump_from_next,
2400
2.85k
            Jump::CalculateOffset(next_step_pos_, visitor_->GetCurrentIndex()));
2401
2402
2.85k
        next_step_->set_jump_offset(jump_from_next);
2403
2.85k
      }
2404
2.85k
      break;
2405
2.85k
    }
2406
2.85k
    case cel::RESULT: {
2407
2.85k
      if (!init_step_ || !next_step_ || !cond_step_) {
2408
        // Encountered an error earlier. Can't determine where to jump.
2409
0
        break;
2410
0
      }
2411
2.85k
      visitor_->AddStep(CreateComprehensionFinishStep(accu_slot_, expr->id()));
2412
      // Set offsets jumping past the result step in case of errors.
2413
2.85k
      CEL_ASSIGN_OR_RETURN(
2414
2.85k
          int jump_from_init,
2415
2.85k
          Jump::CalculateOffset(init_step_pos_, visitor_->GetCurrentIndex()));
2416
2.85k
      init_step_->set_error_jump_offset(jump_from_init);
2417
2418
2.85k
      CEL_ASSIGN_OR_RETURN(
2419
2.85k
          int jump_from_next,
2420
2.85k
          Jump::CalculateOffset(next_step_pos_, visitor_->GetCurrentIndex()));
2421
2.85k
      next_step_->set_error_jump_offset(jump_from_next);
2422
2423
2.85k
      CEL_ASSIGN_OR_RETURN(
2424
2.85k
          int jump_from_cond,
2425
2.85k
          Jump::CalculateOffset(cond_step_pos_, visitor_->GetCurrentIndex()));
2426
2.85k
      cond_step_->set_error_jump_offset(jump_from_cond);
2427
2.85k
      break;
2428
2.85k
    }
2429
14.3k
  }
2430
14.3k
  return absl::OkStatus();
2431
14.3k
}
2432
2433
void ComprehensionVisitor::PostVisitArgTrivial(cel::ComprehensionArg arg_num,
2434
0
                                               const cel::Expr* expr) {
2435
0
  if (visitor_->PlanRecursiveProgram()) {
2436
0
    return;
2437
0
  }
2438
0
  switch (arg_num) {
2439
0
    case cel::ITER_RANGE: {
2440
0
      break;
2441
0
    }
2442
0
    case cel::ACCU_INIT: {
2443
0
      if (!accu_init_extracted_) {
2444
0
        visitor_->AddStep(CreateAssignSlotAndPopStep(accu_slot_));
2445
0
      }
2446
0
      break;
2447
0
    }
2448
0
    case cel::LOOP_CONDITION: {
2449
0
      break;
2450
0
    }
2451
0
    case cel::LOOP_STEP: {
2452
0
      break;
2453
0
    }
2454
0
    case cel::RESULT: {
2455
0
      visitor_->AddStep(CreateClearSlotStep(accu_slot_, expr->id()));
2456
0
      break;
2457
0
    }
2458
0
  }
2459
0
}
2460
2461
2.85k
void ComprehensionVisitor::PostVisit(const cel::Expr* expr) {
2462
2.85k
  if (is_trivial_) {
2463
0
    visitor_->MaybeMakeBindRecursive(expr, &expr->comprehension_expr(),
2464
0
                                     accu_slot_);
2465
0
    return;
2466
0
  }
2467
2.85k
  visitor_->MaybeMakeComprehensionRecursive(
2468
2.85k
      expr, &expr->comprehension_expr(), iter_slot_, iter2_slot_, accu_slot_);
2469
2.85k
}
2470
2471
// Flattens the expression table into the end of the mainline expression vector
2472
// and returns an index to the individual sub expressions.
2473
std::vector<ExecutionPathView> FlattenExpressionTable(
2474
10.3k
    ProgramBuilder& program_builder, ExecutionPath& main) {
2475
10.3k
  std::vector<std::pair<size_t, size_t>> ranges;
2476
10.3k
  main = program_builder.FlattenMain();
2477
10.3k
  ranges.push_back(std::make_pair(0, main.size()));
2478
2479
10.3k
  std::vector<ExecutionPath> subexpressions =
2480
10.3k
      program_builder.FlattenSubexpressions();
2481
10.3k
  for (auto& subexpression : subexpressions) {
2482
0
    ranges.push_back(std::make_pair(main.size(), subexpression.size()));
2483
0
    absl::c_move(subexpression, std::back_inserter(main));
2484
0
  }
2485
2486
10.3k
  std::vector<ExecutionPathView> subexpression_indexes;
2487
10.3k
  subexpression_indexes.reserve(ranges.size());
2488
10.3k
  for (const auto& range : ranges) {
2489
10.3k
    subexpression_indexes.push_back(
2490
10.3k
        absl::MakeSpan(main).subspan(range.first, range.second));
2491
10.3k
  }
2492
10.3k
  return subexpression_indexes;
2493
10.3k
}
2494
2495
absl::Status CheckAstExtensions(
2496
10.7k
    const std::vector<cel::ExtensionSpec>& extensions) {
2497
10.7k
  for (const cel::ExtensionSpec& extension : extensions) {
2498
0
    if (extension.id() == "cel_block" && extension.version().major() == 1) {
2499
      // cel_block v1 is always supported.
2500
0
      continue;
2501
0
    }
2502
2503
    // TODO(uncreated-issue/89): Add support for json field names.
2504
0
    return absl::InvalidArgumentError(absl::StrCat(
2505
0
        "unsupported CEL extension: ", extension.id(), "@",
2506
0
        extension.version().major(), ".", extension.version().minor()));
2507
0
  }
2508
10.7k
  return absl::OkStatus();
2509
10.7k
}
2510
2511
}  // namespace
2512
2513
absl::StatusOr<FlatExpression> FlatExprBuilder::CreateExpressionImpl(
2514
10.7k
    std::unique_ptr<Ast> ast, std::vector<RuntimeIssue>* issues) const {
2515
10.7k
  if (absl::StartsWith(container_, ".") || absl::EndsWith(container_, ".")) {
2516
0
    return absl::InvalidArgumentError(
2517
0
        absl::StrCat("Invalid expression container: '", container_, "'"));
2518
0
  }
2519
2520
10.7k
  RuntimeIssue::Severity max_severity = options_.fail_on_warnings
2521
10.7k
                                            ? RuntimeIssue::Severity::kWarning
2522
10.7k
                                            : RuntimeIssue::Severity::kError;
2523
10.7k
  IssueCollector issue_collector(max_severity);
2524
2525
10.7k
  absl::StatusOr<std::vector<cel::ExtensionSpec>> runtime_extensions =
2526
10.7k
      ExtractAndValidateRuntimeExtensions(*ast);
2527
2528
10.7k
  if (!runtime_extensions.ok()) {
2529
0
    CEL_RETURN_IF_ERROR(issue_collector.AddIssue(
2530
0
        RuntimeIssue::CreateError(runtime_extensions.status())));
2531
0
  }
2532
2533
10.7k
  auto status = CheckAstExtensions(*runtime_extensions);
2534
10.7k
  if (!status.ok()) {
2535
0
    CEL_RETURN_IF_ERROR(
2536
0
        issue_collector.AddIssue(RuntimeIssue::CreateError(status)));
2537
0
  }
2538
2539
10.7k
  Resolver resolver(container_, function_registry_, type_registry_,
2540
10.7k
                    GetTypeProvider(),
2541
10.7k
                    options_.enable_qualified_type_identifiers);
2542
2543
10.7k
  std::shared_ptr<google::protobuf::Arena> arena;
2544
10.7k
  ProgramBuilder program_builder;
2545
10.7k
  PlannerContext extension_context(env_, resolver, options_, GetTypeProvider(),
2546
10.7k
                                   issue_collector, program_builder, arena);
2547
2548
10.7k
  for (const std::unique_ptr<AstTransform>& transform : ast_transforms_) {
2549
10.7k
    CEL_RETURN_IF_ERROR(transform->UpdateAst(extension_context, *ast));
2550
10.7k
  }
2551
2552
10.7k
  std::vector<std::unique_ptr<ProgramOptimizer>> optimizers;
2553
10.7k
  for (const ProgramOptimizerFactory& optimizer_factory : program_optimizers_) {
2554
0
    CEL_ASSIGN_OR_RETURN(auto optimizer,
2555
0
                         optimizer_factory(extension_context, *ast));
2556
0
    if (optimizer != nullptr) {
2557
0
      optimizers.push_back(std::move(optimizer));
2558
0
    }
2559
0
  }
2560
2561
  // These objects are expected to remain scoped to one build call -- references
2562
  // to them shouldn't be persisted in any part of the result expression.
2563
10.7k
  FlatExprVisitor visitor(resolver, options_, std::move(optimizers),
2564
10.7k
                          ast->reference_map(), GetTypeProvider(),
2565
10.7k
                          issue_collector, program_builder, extension_context,
2566
10.7k
                          enable_optional_types_);
2567
2568
10.7k
  if (options_.max_recursion_depth == -1 || options_.max_recursion_depth > 0) {
2569
0
    int depth_limit = options_.max_recursion_depth == -1
2570
0
                          ? std::numeric_limits<int>::max()
2571
0
                          : options_.max_recursion_depth;
2572
0
    visitor.SetMaxRecursionDepth(depth_limit);
2573
0
  }
2574
2575
10.7k
  cel::TraversalOptions opts;
2576
10.7k
  opts.use_comprehension_callbacks = true;
2577
10.7k
  AstTraverse(ast->root_expr(), visitor, opts);
2578
2579
10.7k
  if (!visitor.progress_status().ok()) {
2580
411
    return visitor.progress_status();
2581
411
  }
2582
2583
10.3k
  if (issues != nullptr) {
2584
0
    (*issues) = issue_collector.ExtractIssues();
2585
0
  }
2586
2587
10.3k
  ExecutionPath execution_path;
2588
10.3k
  std::vector<ExecutionPathView> subexpressions =
2589
10.3k
      FlattenExpressionTable(program_builder, execution_path);
2590
2591
10.3k
  return FlatExpression(std::move(execution_path), std::move(subexpressions),
2592
10.3k
                        visitor.slot_count(), GetTypeProvider(), options_,
2593
10.3k
                        std::move(arena));
2594
10.7k
}
2595
42.7k
const cel::TypeProvider& FlatExprBuilder::GetTypeProvider() const {
2596
42.7k
  return use_legacy_type_provider_
2597
42.7k
             ? static_cast<const cel::TypeProvider&>(
2598
42.7k
                   *GetLegacyRuntimeTypeProvider(type_registry_))
2599
42.7k
             : GetRuntimeTypeProvider(type_registry_);
2600
42.7k
}
2601
2602
}  // namespace google::api::expr::runtime