Coverage Report

Created: 2026-09-03 06:30

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