Coverage Report

Created: 2026-10-09 06:57

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/base/nullability.h"
35
#include "absl/container/flat_hash_map.h"
36
#include "absl/container/flat_hash_set.h"
37
#include "absl/container/node_hash_map.h"
38
#include "absl/functional/any_invocable.h"
39
#include "absl/log/absl_check.h"
40
#include "absl/log/check.h"
41
#include "absl/status/status.h"
42
#include "absl/status/statusor.h"
43
#include "absl/strings/match.h"
44
#include "absl/strings/numbers.h"
45
#include "absl/strings/str_cat.h"
46
#include "absl/strings/string_view.h"
47
#include "absl/strings/strip.h"
48
#include "absl/types/span.h"
49
#include "absl/types/variant.h"
50
#include "base/ast.h"
51
#include "base/builtins.h"
52
#include "base/type_provider.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/container_access_step.h"
67
#include "eval/eval/create_list_step.h"
68
#include "eval/eval/create_map_step.h"
69
#include "eval/eval/create_struct_step.h"
70
#include "eval/eval/evaluator_core.h"
71
#include "eval/eval/expression_step_logic.h"
72
#include "eval/eval/function_step.h"
73
#include "eval/eval/optional_or_step.h"
74
#include "eval/eval/select_step.h"
75
#include "eval/eval/shadowable_value_step.h"
76
#include "eval/eval/ternary_step.h"
77
#include "internal/status_macros.h"
78
#include "runtime/internal/convert_constant.h"
79
#include "runtime/internal/issue_collector.h"
80
#include "runtime/runtime_issue.h"
81
#include "runtime/runtime_options.h"
82
#include "runtime/type_registry.h"
83
#include "google/protobuf/arena.h"
84
85
namespace google::api::expr::runtime {
86
87
namespace {
88
89
using ::cel::Ast;
90
using ::cel::AstTraverse;
91
using ::cel::RuntimeIssue;
92
using ::cel::Value;
93
using ::cel::runtime_internal::ConvertConstant;
94
using ::cel::runtime_internal::GetLegacyRuntimeTypeProvider;
95
using ::cel::runtime_internal::GetRuntimeTypeProvider;
96
using ::cel::runtime_internal::IssueCollector;
97
98
constexpr absl::string_view kOptionalOrFn = "or";
99
constexpr absl::string_view kOptionalOrValueFn = "orValue";
100
constexpr absl::string_view kBlock = "cel.@block";
101
102
// Forward declare to resolve circular dependency for short_circuiting visitors.
103
class FlatExprVisitor;
104
105
// Helper for bookkeeping variables mapped to indexes.
106
class IndexManager {
107
 public:
108
17.9k
  IndexManager() : next_free_slot_(0), max_slot_count_(0) {}
109
110
17.1k
  size_t ReserveSlots(size_t n) {
111
17.1k
    size_t result = next_free_slot_;
112
17.1k
    next_free_slot_ += n;
113
17.1k
    if (next_free_slot_ > max_slot_count_) {
114
16.6k
      max_slot_count_ = next_free_slot_;
115
16.6k
    }
116
17.1k
    return result;
117
17.1k
  }
118
119
15.1k
  size_t ReleaseSlots(size_t n) {
120
15.1k
    next_free_slot_ -= n;
121
15.1k
    return next_free_slot_;
122
15.1k
  }
123
124
34.6k
  size_t max_slot_count() const { return max_slot_count_; }
125
126
 private:
127
  size_t next_free_slot_;
128
  size_t max_slot_count_;
129
};
130
131
// Helper for computing jump offsets.
132
//
133
// Jumps should be self-contained to a single expression node -- jumping
134
// outside that range is a bug.
135
struct ProgramStepIndex {
136
  int index;
137
  ProgramBuilder::Subexpression* subexpression;
138
};
139
140
// A convenience wrapper for offset-calculating logic.
141
class Jump {
142
 public:
143
  static absl::StatusOr<int> CalculateOffset(ProgramStepIndex base,
144
102k
                                             ProgramStepIndex target) {
145
102k
    if (target.subexpression != base.subexpression) {
146
0
      return absl::InternalError(
147
0
          "Jump target must be contained in the parent"
148
0
          "subexpression");
149
0
    }
150
151
102k
    int offset = base.subexpression->CalculateOffset(base.index, target.index);
152
153
    // The offset may be packed into a 31-bit int field. Technically, the offset
154
    // could be larger for a well-formed expression with more than 2**30 steps,
155
    // but such a case is unlikely to exist at this time.
156
102k
    constexpr int kMaxJumpOffset = (1 << 30) - 1;
157
102k
    constexpr int kMinJumpOffset = -(1 << 30);
158
159
102k
    if (offset < kMinJumpOffset || offset > kMaxJumpOffset) {
160
0
      return absl::InternalError("Jump offset exceeds supported range: " +
161
0
                                 absl::StrCat(offset));
162
0
    }
163
102k
    return offset;
164
102k
  }
165
};
166
167
class CondVisitor {
168
 public:
169
6.89k
  virtual ~CondVisitor() = default;
170
  virtual void PreVisit(const cel::Expr* expr) = 0;
171
  virtual void PostVisitArg(int arg_num, const cel::Expr* expr) = 0;
172
  virtual void PostVisit(const cel::Expr* expr) = 0;
173
0
  virtual void PostVisitTarget(const cel::Expr* expr) {}
174
};
175
176
// Visitor managing the "&&" and "||" (boolean logic) operations.
177
// Implements short-circuiting if enabled.
178
//
179
// With short-circuiting enabled, generates a program like:
180
//   +-------------+------------------------+-----------------------+
181
//   | PC          | Step                   | Stack                 |
182
//   +-------------+------------------------+-----------------------+
183
//   | i + 0       | <Arg1>                 | arg1                  |
184
//   | i + 1       | ConditionalJump i + 4  | arg1                  |
185
//   | i + 2       | <Arg2>                 | arg1, arg2            |
186
//   | i + 3       | BooleanOperator        | Op(arg1, arg2)        |
187
//   | i + 4       | <rest of program>      | arg1 | Op(arg1, arg2) |
188
//   +-------------+------------------------+------------------------+
189
class LogicalCondVisitor : public CondVisitor {
190
 public:
191
  explicit LogicalCondVisitor(FlatExprVisitor* visitor, bool is_or,
192
                              bool short_circuiting)
193
4.42k
      : visitor_(visitor), is_or_(is_or), short_circuiting_(short_circuiting) {}
194
195
  void PreVisit(const cel::Expr* expr) override;
196
  void PostVisitArg(int arg_num, const cel::Expr* expr) override;
197
  void PostVisit(const cel::Expr* expr) override;
198
199
 private:
200
  FlatExprVisitor* visitor_;
201
  const bool is_or_;
202
  std::vector<ProgramStepIndex> jump_steps_;
203
  bool short_circuiting_;
204
};
205
206
// Visitor managing optional "or" and "orValue" operations.
207
// Implements short-circuiting if enabled.
208
class OptionalOrCondVisitor : public CondVisitor {
209
 public:
210
  explicit OptionalOrCondVisitor(FlatExprVisitor* visitor, bool is_or_value,
211
                                 bool short_circuiting)
212
0
      : visitor_(visitor),
213
0
        is_or_value_(is_or_value),
214
0
        short_circuiting_(short_circuiting) {}
215
216
  void PreVisit(const cel::Expr* expr) override;
217
0
  void PostVisitArg(int arg_num, const cel::Expr* expr) override {}
218
  void PostVisitTarget(const cel::Expr* expr) override;
219
  void PostVisit(const cel::Expr* expr) override;
220
221
 private:
222
  FlatExprVisitor* visitor_;
223
  const bool is_or_value_;
224
  OptionalHasValueJumpStep* jump_step_ = nullptr;
225
  ProgramStepIndex jump_step_pos_{-1, nullptr};
226
  bool short_circuiting_;
227
};
228
229
class TernaryCondVisitor : public CondVisitor {
230
 public:
231
2.47k
  explicit TernaryCondVisitor(FlatExprVisitor* visitor) : visitor_(visitor) {}
232
233
  void PreVisit(const cel::Expr* expr) override;
234
  void PostVisitArg(int arg_num, const cel::Expr* expr) override;
235
  void PostVisit(const cel::Expr* expr) override;
236
237
 private:
238
  FlatExprVisitor* visitor_;
239
  std::optional<ProgramStepIndex> cond_jump_pos_;
240
  std::optional<ProgramStepIndex> jump_after_first_pos_;
241
};
242
243
class ExhaustiveTernaryCondVisitor : public CondVisitor {
244
 public:
245
  explicit ExhaustiveTernaryCondVisitor(FlatExprVisitor* visitor)
246
0
      : visitor_(visitor) {}
247
248
  void PreVisit(const cel::Expr* expr) override;
249
0
  void PostVisitArg(int arg_num, const cel::Expr* expr) override {}
250
  void PostVisit(const cel::Expr* expr) override;
251
252
 private:
253
  FlatExprVisitor* visitor_;
254
};
255
256
// Returns a hint for the number of program nodes (steps or subexpressions) that
257
// will be created for this expr.
258
457k
size_t SizeHint(const cel::Expr& expr) {
259
457k
  switch (expr.kind_case()) {
260
157k
    case cel::ExprKindCase::kConstant:
261
157k
      return 1;
262
89.7k
    case cel::ExprKindCase::kIdentExpr:
263
89.7k
      return 1;
264
16.7k
    case cel::ExprKindCase::kSelectExpr:
265
16.7k
      return 2;
266
119k
    case cel::ExprKindCase::kCallExpr:
267
119k
      return expr.call_expr().args().size() +
268
119k
             (expr.call_expr().has_target() ? 2 : 1);
269
50.8k
    case cel::ExprKindCase::kListExpr:
270
50.8k
      return expr.list_expr().elements().size() + 1;
271
2.91k
    case cel::ExprKindCase::kStructExpr:
272
2.91k
      return expr.struct_expr().fields().size() + 1;
273
4.21k
    case cel::ExprKindCase::kMapExpr:
274
4.21k
      return 2 * expr.struct_expr().fields().size() + 1;
275
17.1k
    default:
276
17.1k
      return 1;
277
457k
  }
278
0
  return 0;
279
457k
}
280
281
// Returns whether this comprehension appears to be a standard map/filter
282
// macro implementation. It is not exhaustive, so it is unsafe to use with
283
// custom comprehensions outside of the standard macros or hand crafted ASTs.
284
bool IsOptimizableListAppend(const cel::ComprehensionExpr* comprehension,
285
17.1k
                             bool enable_comprehension_list_append) {
286
17.1k
  if (!enable_comprehension_list_append) {
287
17.1k
    return false;
288
17.1k
  }
289
0
  absl::string_view accu_var = comprehension->accu_var();
290
0
  if (accu_var.empty() ||
291
0
      comprehension->result().ident_expr().name() != accu_var) {
292
0
    return false;
293
0
  }
294
0
  if (!comprehension->accu_init().has_list_expr() ||
295
0
      !comprehension->accu_init().list_expr().elements().empty()) {
296
0
    return false;
297
0
  }
298
299
0
  if (!comprehension->loop_step().has_call_expr()) {
300
0
    return false;
301
0
  }
302
303
  // Macro loop_step for a filter() will contain a ternary:
304
  //   filter ? accu_var + [elem] : accu_var
305
  // Macro loop_step for a map() will contain a list concat operation:
306
  //   accu_var + [elem]
307
0
  const auto* call_expr = &comprehension->loop_step().call_expr();
308
309
0
  if (call_expr->function() == cel::builtin::kTernary &&
310
0
      call_expr->args().size() == 3) {
311
0
    if (!call_expr->args()[1].has_call_expr()) {
312
0
      return false;
313
0
    }
314
0
    call_expr = &(call_expr->args()[1].call_expr());
315
0
  }
316
317
0
  return call_expr->function() == cel::builtin::kAdd &&
318
0
         call_expr->args().size() == 2 &&
319
0
         call_expr->args()[0].has_ident_expr() &&
320
0
         call_expr->args()[0].ident_expr().name() == accu_var &&
321
0
         call_expr->args()[1].has_list_expr() &&
322
0
         call_expr->args()[1].list_expr().elements().size() == 1;
323
0
}
324
325
// Assuming `IsOptimizableListAppend()` return true, return a pointer to the
326
// call `accu_var + [elem]`.
327
const cel::CallExpr* GetOptimizableListAppendCall(
328
0
    const cel::ComprehensionExpr* comprehension) {
329
0
  ABSL_DCHECK(IsOptimizableListAppend(
330
0
      comprehension, /*enable_comprehension_list_append=*/true));
331
332
  // Macro loop_step for a filter() will contain a ternary:
333
  //   filter ? accu_var + [elem] : accu_var
334
  // Macro loop_step for a map() will contain a list concat operation:
335
  //   accu_var + [elem]
336
0
  const auto* call_expr = &comprehension->loop_step().call_expr();
337
338
0
  if (call_expr->function() == cel::builtin::kTernary &&
339
0
      call_expr->args().size() == 3) {
340
0
    call_expr = &(call_expr->args()[1].call_expr());
341
0
  }
342
0
  return call_expr;
343
0
}
344
345
// Assuming `IsOptimizableListAppend()` return true, return a pointer to the
346
// node `[elem]`.
347
const cel::Expr* GetOptimizableListAppendOperand(
348
0
    const cel::ComprehensionExpr* comprehension) {
349
0
  return &GetOptimizableListAppendCall(comprehension)->args()[1];
350
0
}
351
352
// Returns whether this comprehension appears to be a macro implementation for
353
// map transformations. It is not exhaustive, so it is unsafe to use with custom
354
// comprehensions outside of the standard macros or hand crafted ASTs.
355
bool IsOptimizableMapInsert(const cel::ComprehensionExpr* comprehension,
356
17.1k
                            bool enable_comprehension_mutable_map) {
357
17.1k
  if (!enable_comprehension_mutable_map) {
358
17.1k
    return false;
359
17.1k
  }
360
0
  if (comprehension->iter_var().empty() || comprehension->iter_var2().empty()) {
361
0
    return false;
362
0
  }
363
0
  absl::string_view accu_var = comprehension->accu_var();
364
0
  if (accu_var.empty() || !comprehension->has_result() ||
365
0
      !comprehension->result().has_ident_expr() ||
366
0
      comprehension->result().ident_expr().name() != accu_var) {
367
0
    return false;
368
0
  }
369
0
  if (!comprehension->accu_init().has_map_expr()) {
370
0
    return false;
371
0
  }
372
0
  if (!comprehension->loop_step().has_call_expr()) {
373
0
    return false;
374
0
  }
375
0
  const auto* call_expr = &comprehension->loop_step().call_expr();
376
377
0
  if (call_expr->function() == cel::builtin::kTernary &&
378
0
      call_expr->args().size() == 3) {
379
0
    if (!call_expr->args()[1].has_call_expr()) {
380
0
      return false;
381
0
    }
382
0
    call_expr = &(call_expr->args()[1].call_expr());
383
0
  }
384
0
  return call_expr->function() == "cel.@mapInsert" &&
385
0
         (call_expr->args().size() == 2 || call_expr->args().size() == 3) &&
386
0
         call_expr->args()[0].has_ident_expr() &&
387
0
         call_expr->args()[0].ident_expr().name() == accu_var;
388
0
}
389
390
17.1k
bool IsBind(const cel::ComprehensionExpr* comprehension) {
391
17.1k
  static constexpr absl::string_view kUnusedIterVar = "#unused";
392
393
17.1k
  return comprehension->loop_condition().const_expr().has_bool_value() &&
394
16.9k
         comprehension->loop_condition().const_expr().bool_value() == false &&
395
0
         comprehension->iter_var() == kUnusedIterVar &&
396
0
         comprehension->iter_var2().empty() &&
397
0
         comprehension->iter_range().has_list_expr() &&
398
0
         comprehension->iter_range().list_expr().elements().empty();
399
17.1k
}
400
401
112k
bool IsBlock(const cel::CallExpr* call) { return call->function() == kBlock; }
402
403
// Visitor for Comprehension expressions.
404
class ComprehensionVisitor {
405
 public:
406
  explicit ComprehensionVisitor(FlatExprVisitor* visitor, bool is_trivial,
407
                                size_t iter_slot, size_t iter2_slot,
408
                                size_t accu_slot)
409
17.1k
      : visitor_(visitor),
410
17.1k
        init_step_(nullptr),
411
17.1k
        is_trivial_(is_trivial),
412
17.1k
        accu_init_extracted_(false),
413
17.1k
        iter_slot_(iter_slot),
414
17.1k
        iter2_slot_(iter2_slot),
415
17.1k
        accu_slot_(accu_slot) {}
416
417
  void PreVisit(const cel::Expr* expr);
418
  absl::Status PostVisitArg(cel::ComprehensionArg arg_num,
419
75.8k
                            const cel::Expr* comprehension_expr) {
420
75.8k
    if (is_trivial_) {
421
0
      PostVisitArgTrivial(arg_num, comprehension_expr);
422
0
      return absl::OkStatus();
423
75.8k
    } else {
424
75.8k
      return PostVisitArgDefault(arg_num, comprehension_expr);
425
75.8k
    }
426
75.8k
  }
427
  void PostVisit(const cel::Expr* expr);
428
429
0
  void MarkAccuInitExtracted() { accu_init_extracted_ = true; }
430
431
 private:
432
  void PostVisitArgTrivial(cel::ComprehensionArg arg_num,
433
                           const cel::Expr* comprehension_expr);
434
435
  absl::Status PostVisitArgDefault(cel::ComprehensionArg arg_num,
436
                                   const cel::Expr* comprehension_expr);
437
438
  ComprehensionCondStep* absl_nullable GetCondStep();
439
  ComprehensionNextStep* absl_nullable GetNextStep();
440
441
  FlatExprVisitor* visitor_;
442
  ComprehensionInitStep* init_step_;
443
  ProgramStepIndex init_step_pos_;
444
  std::optional<ProgramStepIndex> next_step_pos_;
445
  std::optional<ProgramStepIndex> cond_step_pos_;
446
  bool is_trivial_;
447
  bool accu_init_extracted_;
448
  size_t iter_slot_;
449
  size_t iter2_slot_;
450
  size_t accu_slot_;
451
};
452
453
absl::flat_hash_set<int32_t> MakeOptionalIndicesSet(
454
2.56k
    const cel::StructExpr& create_struct_expr) {
455
2.56k
  absl::flat_hash_set<int32_t> optional_indices;
456
3.76k
  for (size_t i = 0; i < create_struct_expr.fields().size(); ++i) {
457
1.19k
    if (create_struct_expr.fields()[i].optional()) {
458
0
      optional_indices.insert(static_cast<int32_t>(i));
459
0
    }
460
1.19k
  }
461
2.56k
  return optional_indices;
462
2.56k
}
463
464
absl::flat_hash_set<int32_t> MakeOptionalIndicesSet(
465
4.60k
    const cel::MapExpr& map_expr) {
466
4.60k
  absl::flat_hash_set<int32_t> optional_indices;
467
9.71k
  for (size_t i = 0; i < map_expr.entries().size(); ++i) {
468
5.10k
    if (map_expr.entries()[i].optional()) {
469
0
      optional_indices.insert(static_cast<int32_t>(i));
470
0
    }
471
5.10k
  }
472
4.60k
  return optional_indices;
473
4.60k
}
474
475
class FlatExprVisitor : public cel::AstVisitor {
476
 public:
477
  enum class CallHandlerResult {
478
    // The call was intercepted, no additional processing is needed.
479
    kIntercepted,
480
    // The call was not intercepted, continue with the default processing.
481
    kNotIntercepted,
482
  };
483
484
  // Handler for functions with builtin implementations.
485
  // This is used to replace the usual dispatcher step that applies
486
  // the arguments to a candidate function from the function registry.
487
  using CallHandler = absl::AnyInvocable<CallHandlerResult(
488
      const cel::Expr&, const cel::CallExpr&)>;
489
490
  FlatExprVisitor(
491
      const Resolver& resolver, const cel::RuntimeOptions& options,
492
      std::vector<std::unique_ptr<ProgramOptimizer>> program_optimizers,
493
      const absl::flat_hash_map<int64_t, cel::TypeSpec>& type_map,
494
      const cel::TypeProvider& type_provider, IssueCollector& issue_collector,
495
      ProgramBuilder& program_builder, PlannerContext& extension_context,
496
      bool enable_optional_types)
497
17.9k
      : resolver_(resolver),
498
17.9k
        type_provider_(type_provider),
499
17.9k
        progress_status_(absl::OkStatus()),
500
17.9k
        resolved_select_expr_(nullptr),
501
17.9k
        options_(options),
502
17.9k
        program_optimizers_(std::move(program_optimizers)),
503
17.9k
        type_map_(type_map),
504
17.9k
        issue_collector_(issue_collector),
505
17.9k
        program_builder_(program_builder),
506
17.9k
        extension_context_(extension_context),
507
17.9k
        enable_optional_types_(enable_optional_types) {
508
17.9k
    constexpr size_t kCallHandlerSizeHint = 11;
509
17.9k
    call_handlers_.reserve(kCallHandlerSizeHint);
510
17.9k
    call_handlers_[cel::builtin::kIndex] = [this](const cel::Expr& expr,
511
17.9k
                                                  const cel::CallExpr& call) {
512
3.02k
      return HandleIndex(expr, call);
513
3.02k
    };
514
17.9k
    call_handlers_[kBlock] = [this](const cel::Expr& expr,
515
17.9k
                                    const cel::CallExpr& call) {
516
0
      return HandleBlock(expr, call);
517
0
    };
518
17.9k
    call_handlers_[cel::builtin::kAdd] = [this](const cel::Expr& expr,
519
21.1k
                                                const cel::CallExpr& call) {
520
21.1k
      return HandleListAppend(expr, call);
521
21.1k
    };
522
17.9k
    if (options_.enable_fast_builtins) {
523
17.9k
      call_handlers_[cel::builtin::kNotStrictlyFalse] =
524
17.9k
          [this](const cel::Expr& expr, const cel::CallExpr& call) {
525
154
            return HandleNotStrictlyFalse(expr, call);
526
154
          };
527
17.9k
      call_handlers_[cel::builtin::kNotStrictlyFalseDeprecated] =
528
17.9k
          [this](const cel::Expr& expr, const cel::CallExpr& call) {
529
0
            return HandleNotStrictlyFalse(expr, call);
530
0
          };
531
17.9k
      call_handlers_[cel::builtin::kNot] = [this](const cel::Expr& expr,
532
17.9k
                                                  const cel::CallExpr& call) {
533
638
        return HandleNot(expr, call);
534
638
      };
535
17.9k
      if (options_.enable_heterogeneous_equality) {
536
17.9k
        for (const auto& in_op :
537
17.9k
             {cel::builtin::kIn, cel::builtin::kInDeprecated,
538
53.7k
              cel::builtin::kInFunction}) {
539
53.7k
          call_handlers_[in_op] = [this](const cel::Expr& expr,
540
53.7k
                                         const cel::CallExpr& call) {
541
1.78k
            return HandleHeterogeneousEqualityIn(expr, call);
542
1.78k
          };
543
53.7k
        }
544
        // Try to detect if the environment is setup with a custom equality
545
        // implementation.
546
17.9k
        if (resolver_
547
17.9k
                .FindOverloads(cel::builtin::kEqual,
548
17.9k
                               /*receiver_style=*/false,
549
17.9k
                               {cel::Kind::kAny, cel::Kind::kAny})
550
17.9k
                .empty()) {
551
17.9k
          call_handlers_[cel::builtin::kEqual] =
552
17.9k
              [this](const cel::Expr& expr, const cel::CallExpr& call) {
553
9.69k
                return HandleHeterogeneousEquality(expr, call,
554
9.69k
                                                   /*inequality=*/false);
555
9.69k
              };
556
17.9k
          call_handlers_[cel::builtin::kInequal] =
557
17.9k
              [this](const cel::Expr& expr, const cel::CallExpr& call) {
558
289
                return HandleHeterogeneousEquality(expr, call,
559
289
                                                   /*inequality=*/true);
560
289
              };
561
17.9k
        }
562
17.9k
      }
563
17.9k
    }
564
17.9k
  }
565
566
0
  void SetResolvedType(const cel::Expr& expr, cel::Type type) {
567
0
    resolved_types_[&expr] = std::move(type);
568
0
  }
569
570
16.3k
  std::optional<cel::Type> GetResolvedType(const cel::Expr* expr) const {
571
16.3k
    if (expr == nullptr) {
572
0
      return std::nullopt;
573
0
    }
574
16.3k
    auto it = resolved_types_.find(expr);
575
16.3k
    if (it != resolved_types_.end()) {
576
0
      return it->second;
577
0
    }
578
16.3k
    return std::nullopt;
579
16.3k
  }
580
581
484k
  void PreVisitExpr(const cel::Expr& expr) override {
582
484k
    ValidateOrError(!absl::holds_alternative<cel::UnspecifiedExpr>(expr.kind()),
583
484k
                    "Invalid empty expression");
584
484k
    if (!progress_status_.ok()) {
585
26.5k
      return;
586
26.5k
    }
587
457k
    if (resume_from_suppressed_branch_ == nullptr &&
588
457k
        suppressed_branches_.find(&expr) != suppressed_branches_.end()) {
589
0
      resume_from_suppressed_branch_ = &expr;
590
0
    }
591
592
457k
    if (options_.enable_typed_field_access) {
593
457k
      MaybeResolveType(expr);
594
457k
    }
595
596
457k
    if (block_.has_value()) {
597
0
      BlockInfo& block = *block_;
598
0
      if (block.in && block.bindings_set.contains(&expr)) {
599
0
        block.current_binding = &expr;
600
0
      }
601
0
    }
602
603
457k
    auto* subexpression =
604
457k
        program_builder_.EnterSubexpression(&expr, SizeHint(expr));
605
457k
    if (subexpression == nullptr) {
606
0
      progress_status_.Update(
607
0
          absl::InternalError("same CEL expr visited twice"));
608
0
      return;
609
0
    }
610
611
457k
    for (const std::unique_ptr<ProgramOptimizer>& optimizer :
612
457k
         program_optimizers_) {
613
0
      absl::Status status = optimizer->OnPreVisit(extension_context_, expr);
614
0
      if (!status.ok()) {
615
0
        SetProgressStatusIfError(status);
616
0
      }
617
0
    }
618
457k
  }
619
620
484k
  void PostVisitExpr(const cel::Expr& expr) override {
621
484k
    if (!progress_status_.ok()) {
622
31.9k
      return;
623
31.9k
    }
624
452k
    if (&expr == resume_from_suppressed_branch_) {
625
0
      resume_from_suppressed_branch_ = nullptr;
626
0
    }
627
628
452k
    for (const std::unique_ptr<ProgramOptimizer>& optimizer :
629
452k
         program_optimizers_) {
630
0
      absl::Status status = optimizer->OnPostVisit(extension_context_, expr);
631
0
      if (!status.ok()) {
632
0
        SetProgressStatusIfError(status);
633
0
        return;
634
0
      }
635
0
    }
636
637
452k
    program_builder_.ExitSubexpression(&expr);
638
639
452k
    if (!comprehension_stack_.empty() &&
640
314k
        comprehension_stack_.back().is_optimizable_bind &&
641
0
        (&comprehension_stack_.back().comprehension->accu_init() == &expr)) {
642
0
      SetProgressStatusIfError(
643
0
          MaybeExtractSubexpression(&expr, comprehension_stack_.back()));
644
0
    }
645
646
452k
    if (block_.has_value()) {
647
0
      BlockInfo& block = *block_;
648
0
      if (block.current_binding == &expr) {
649
0
        int index = program_builder_.ExtractSubexpression(&expr);
650
0
        if (index == -1) {
651
0
          SetProgressStatusIfError(
652
0
              absl::InvalidArgumentError("failed to extract subexpression"));
653
0
          return;
654
0
        }
655
0
        block.subexpressions[block.current_index++] = index;
656
0
        block.current_binding = nullptr;
657
0
      }
658
0
    }
659
452k
  }
660
661
  void PostVisitConst(const cel::Expr& expr,
662
160k
                      const cel::Constant& const_expr) override {
663
160k
    if (!progress_status_.ok()) {
664
2.83k
      return;
665
2.83k
    }
666
667
157k
    absl::StatusOr<cel::Value> converted_value =
668
157k
        ConvertConstant(const_expr, extension_context_.MutableArena());
669
670
157k
    if (!converted_value.ok()) {
671
0
      SetProgressStatusIfError(converted_value.status());
672
0
      return;
673
0
    }
674
675
157k
    AddStep(ExpressionStep::MakeConstant(std::move(converted_value).value(),
676
157k
                                         expr.id()));
677
157k
  }
678
679
  struct SlotLookupResult {
680
    int slot;
681
    int subexpression;
682
  };
683
684
  // Helper to lookup a variable mapped to a slot.
685
  //
686
  // If lazy evaluation enabled and ided as a lazy expression,
687
  // subexpression and slot will be set.
688
89.7k
  SlotLookupResult LookupSlot(absl::string_view path) {
689
    // If there's a leading dot, it cannot resolve to a local variable.
690
89.7k
    if (absl::StartsWith(path, ".")) {
691
705
      return {-1, -1};
692
705
    }
693
89.0k
    if (block_.has_value()) {
694
0
      const BlockInfo& block = *block_;
695
0
      if (block.in) {
696
0
        absl::string_view index_suffix = path;
697
0
        if (absl::ConsumePrefix(&index_suffix, "@index")) {
698
0
          size_t index;
699
0
          if (!absl::SimpleAtoi(index_suffix, &index)) {
700
0
            SetProgressStatusIfError(
701
0
                issue_collector_.AddIssue(RuntimeIssue::CreateError(
702
0
                    absl::InvalidArgumentError("bad @index"))));
703
0
            return {-1, -1};
704
0
          }
705
0
          if (index >= block.size) {
706
0
            SetProgressStatusIfError(
707
0
                issue_collector_.AddIssue(RuntimeIssue::CreateError(
708
0
                    absl::InvalidArgumentError(absl::StrCat(
709
0
                        "invalid @index greater than number of bindings: ",
710
0
                        index, " >= ", block.size)))));
711
0
            return {-1, -1};
712
0
          }
713
0
          if (index >= block.current_index) {
714
0
            SetProgressStatusIfError(
715
0
                issue_collector_.AddIssue(RuntimeIssue::CreateError(
716
0
                    absl::InvalidArgumentError(absl::StrCat(
717
0
                        "@index references current or future binding: ", index,
718
0
                        " >= ", block.current_index)))));
719
0
            return {-1, -1};
720
0
          }
721
0
          return {static_cast<int>(block.index + index),
722
0
                  block.subexpressions[index]};
723
0
        }
724
0
      }
725
0
    }
726
89.0k
    if (!comprehension_stack_.empty()) {
727
224k
      for (int i = comprehension_stack_.size() - 1; i >= 0; i--) {
728
199k
        const ComprehensionStackRecord& record = comprehension_stack_[i];
729
199k
        if (record.iter_var_in_scope &&
730
62.8k
            record.comprehension->iter_var() == path) {
731
5.27k
          if (record.is_optimizable_bind) {
732
0
            SetProgressStatusIfError(issue_collector_.AddIssue(
733
0
                RuntimeIssue::CreateWarning(absl::InvalidArgumentError(
734
0
                    "Unexpected iter_var access in trivial comprehension"))));
735
0
            return {-1, -1};
736
0
          }
737
5.27k
          return {static_cast<int>(record.iter_slot), -1};
738
5.27k
        }
739
193k
        if (record.iter_var2_in_scope &&
740
57.5k
            record.comprehension->iter_var2() == path) {
741
0
          return {static_cast<int>(record.iter2_slot), -1};
742
0
        }
743
193k
        if (record.accu_var_in_scope &&
744
72.6k
            record.comprehension->accu_var() == path) {
745
32.5k
          int slot = record.accu_slot;
746
32.5k
          int subexpression = -1;
747
32.5k
          if (record.is_optimizable_bind) {
748
0
            subexpression = record.subexpression;
749
0
          }
750
32.5k
          return {slot, subexpression};
751
32.5k
        }
752
193k
      }
753
63.1k
    }
754
51.3k
    if (absl::StartsWith(path, "@it:") || absl::StartsWith(path, "@it2:") ||
755
51.3k
        absl::StartsWith(path, "@ac:")) {
756
      // If we see a CSE generated comprehension variable that was not
757
      // resolvable through the normal comprehension scope resolution, reject it
758
      // now rather than surfacing errors at activation time.
759
0
      SetProgressStatusIfError(
760
0
          issue_collector_.AddIssue(RuntimeIssue::CreateError(
761
0
              absl::InvalidArgumentError("out of scope reference to CSE "
762
0
                                         "generated comprehension variable"))));
763
0
    }
764
51.3k
    return {-1, -1};
765
89.0k
  }
766
767
  // Ident node handler.
768
  // Invoked after child nodes are processed.
769
  void PostVisitIdent(const cel::Expr& expr,
770
101k
                      const cel::IdentExpr& ident_expr) override {
771
101k
    if (!progress_status_.ok()) {
772
11.7k
      return;
773
11.7k
    }
774
89.7k
    absl::string_view path = ident_expr.name();
775
89.7k
    if (!ValidateOrError(
776
89.7k
            !path.empty(),
777
89.7k
            "Invalid expression: identifier 'name' must not be empty")) {
778
0
      return;
779
0
    }
780
781
    // Check if this is a local variable first (since it should shadow most
782
    // other interpretations).
783
89.7k
    SlotLookupResult slot = LookupSlot(path);
784
785
89.7k
    if (slot.subexpression >= 0) {
786
0
      auto* subexpression =
787
0
          program_builder_.GetExtractedSubexpression(slot.subexpression);
788
0
      if (subexpression == nullptr) {
789
0
        SetProgressStatusIfError(
790
0
            absl::InternalError("bad subexpression reference"));
791
0
        return;
792
0
      }
793
        // Off by one since mainline expression will be index 0.
794
0
        AddStep(ExpressionStep::MakeLazyInitStep(
795
0
            slot.slot, slot.subexpression + 1, expr.id()));
796
0
      return;
797
89.7k
    } else if (slot.slot >= 0) {
798
37.7k
        AddStep(ExpressionStep::MakeReadSlotStep(slot.slot, expr.id()));
799
37.7k
      return;
800
37.7k
    }
801
802
    // Attempt to resolve a select expression as a namespaced identifier for an
803
    // enum or type constant value.
804
52.0k
    std::optional<cel::Value> const_value;
805
52.0k
    int64_t select_root_id = -1;
806
52.0k
    std::string path_candidate;
807
808
60.3k
    while (!namespace_stack_.empty()) {
809
8.38k
      const auto& select_node = namespace_stack_.front();
810
      // Generate path in format "<ident>.<field 0>.<field 1>...".
811
8.38k
      const cel::Expr* select_expr = select_node.first;
812
8.38k
      path_candidate = absl::StrCat(path, ".", select_node.second);
813
814
      // Attempt to find a constant enum or type value which matches the
815
      // qualified path present in the expression. Whether the identifier
816
      // can be resolved to a type instance depends on whether the option to
817
      // 'enable_qualified_type_identifiers' is set to true.
818
8.38k
      const_value = resolver_.FindConstant(path_candidate, select_expr->id());
819
8.38k
      if (const_value) {
820
55
        resolved_select_expr_ = select_expr;
821
55
        select_root_id = select_expr->id();
822
55
        path = path_candidate;
823
55
        namespace_stack_.clear();
824
55
        break;
825
55
      }
826
8.32k
      namespace_stack_.pop_front();
827
8.32k
    }
828
829
52.0k
    if (!const_value) {
830
      // Attempt to resolve a simple identifier as an enum or type constant
831
      // value.
832
51.9k
      const_value = resolver_.FindConstant(path, expr.id());
833
51.9k
      select_root_id = expr.id();
834
51.9k
    }
835
836
    // TODO(issues/97): Need to add support for resolving packaged names at
837
    // runtime if Parse-only. For checked, checker should have reported the
838
    // expected interpretation.
839
52.0k
    if (const_value) {
840
      // If the path starts with a dot, strip it.
841
535
      absl::string_view name = absl::StripPrefix(path, ".");
842
535
      AddStep(CreateShadowableValueStep(name, std::move(const_value).value()),
843
535
              select_root_id);
844
535
      return;
845
535
    }
846
847
51.4k
    absl::string_view ident_name = absl::StripPrefix(ident_expr.name(), ".");
848
51.4k
      AddStep(ExpressionStep::MakeIdentifierStep(ident_name, expr.id()));
849
51.4k
  }
850
851
  void PreVisitSelect(const cel::Expr& expr,
852
17.1k
                      const cel::SelectExpr& select_expr) override {
853
17.1k
    if (!progress_status_.ok()) {
854
371
      return;
855
371
    }
856
16.7k
    if (!ValidateOrError(
857
16.7k
            !select_expr.field().empty(),
858
16.7k
            "invalid expression: select 'field' must not be empty")) {
859
0
      return;
860
0
    }
861
16.7k
    if (!ValidateOrError(
862
16.7k
            select_expr.has_operand() &&
863
16.7k
                select_expr.operand().kind_case() !=
864
16.7k
                    cel::ExprKindCase::kUnspecifiedExpr,
865
16.7k
            "invalid expression: select must specify an operand")) {
866
0
      return;
867
0
    }
868
869
    // Not exactly the cleanest solution - we peek into child of
870
    // select_expr.
871
    // Chain of multiple SELECT ending with IDENT can represent namespaced
872
    // entity.
873
16.7k
    if (!select_expr.test_only() && (select_expr.operand().has_ident_expr() ||
874
15.5k
                                     select_expr.operand().has_select_expr())) {
875
      // select expressions are pushed in reverse order:
876
      // google.type.Expr is pushed as:
877
      // - field: 'Expr'
878
      // - field: 'type'
879
      // - id: 'google'
880
      //
881
      // The search order though is as follows:
882
      // - id: 'google.type.Expr'
883
      // - id: 'google.type', field: 'Expr'
884
      // - id: 'google', field: 'type', field: 'Expr'
885
432k
      for (size_t i = 0; i < namespace_stack_.size(); i++) {
886
421k
        auto ns = namespace_stack_[i];
887
421k
        namespace_stack_[i] = {
888
421k
            ns.first, absl::StrCat(select_expr.field(), ".", ns.second)};
889
421k
      }
890
11.1k
      namespace_stack_.push_back({&expr, select_expr.field()});
891
11.1k
    } else {
892
5.60k
      namespace_stack_.clear();
893
5.60k
    }
894
16.7k
  }
895
896
  // Select node handler.
897
  // Invoked after child nodes are processed.
898
  void PostVisitSelect(const cel::Expr& expr,
899
17.1k
                       const cel::SelectExpr& select_expr) override {
900
17.1k
    if (!progress_status_.ok()) {
901
645
      return;
902
645
    }
903
904
    // Check if we are "in the middle" of namespaced name.
905
    // This is currently enum specific. Constant expression that corresponds
906
    // to resolved enum value has been already created, thus preceding chain
907
    // of selects is no longer relevant.
908
16.4k
    if (resolved_select_expr_) {
909
165
      if (&expr == resolved_select_expr_) {
910
55
        resolved_select_expr_ = nullptr;
911
55
      }
912
165
      return;
913
165
    }
914
915
16.3k
    std::string field = select_expr.field();
916
16.3k
    std::optional<cel::StructType> struct_type;
917
16.3k
    std::optional<cel::StructTypeField> field_type;
918
16.3k
    if (options_.enable_typed_field_access) {
919
16.3k
      std::optional<cel::Type> operand_type =
920
16.3k
          GetResolvedType(&select_expr.operand());
921
16.3k
      if (operand_type.has_value() && operand_type->IsStruct()) {
922
0
        struct_type = operand_type->GetStruct();
923
0
        if (struct_type.has_value()) {
924
0
          auto field_lookup =
925
0
              extension_context_.type_reflector().FindStructTypeFieldByName(
926
0
                  *struct_type, select_expr.field());
927
          // Swallow error to fallback to duck typing behavior.
928
0
          if (field_lookup.ok() && field_lookup->has_value()) {
929
0
            field_type = *std::move(field_lookup);
930
0
          }
931
0
        }
932
0
      }
933
16.3k
    }
934
935
16.3k
    if (field_type.has_value()) {
936
0
      AddStep(
937
0
          CreateTypedSelectStep(std::move(field), *struct_type,
938
0
                                *std::move(field_type), select_expr.test_only(),
939
0
                                options_.enable_empty_wrapper_null_unboxing,
940
0
                                enable_optional_types_),
941
0
          expr.id());
942
0
      return;
943
0
    }
944
16.3k
    AddStep(CreateSelectStep(std::move(field), select_expr.test_only(),
945
16.3k
                             options_.enable_empty_wrapper_null_unboxing,
946
16.3k
                             enable_optional_types_),
947
16.3k
            expr.id());
948
16.3k
  }
949
950
  // Call node handler group.
951
  // We provide finer granularity for Call node callbacks to allow special
952
  // handling for short-circuiting
953
  // PreVisitCall is invoked before child nodes are processed.
954
  void PreVisitCall(const cel::Expr& expr,
955
124k
                    const cel::CallExpr& call_expr) override {
956
124k
    if (!progress_status_.ok()) {
957
5.19k
      return;
958
5.19k
    }
959
960
119k
    std::unique_ptr<CondVisitor> cond_visitor;
961
119k
    if (call_expr.function() == cel::builtin::kAnd) {
962
1.72k
      cond_visitor = std::make_unique<LogicalCondVisitor>(
963
1.72k
          this, /*is_or=*/false, options_.short_circuiting);
964
117k
    } else if (call_expr.function() == cel::builtin::kOr) {
965
2.69k
      cond_visitor = std::make_unique<LogicalCondVisitor>(
966
2.69k
          this, /*is_or=*/true, options_.short_circuiting);
967
114k
    } else if (call_expr.function() == cel::builtin::kTernary) {
968
2.47k
      if (options_.short_circuiting) {
969
2.47k
        cond_visitor = std::make_unique<TernaryCondVisitor>(this);
970
2.47k
      } else {
971
0
        cond_visitor = std::make_unique<ExhaustiveTernaryCondVisitor>(this);
972
0
      }
973
112k
    } else if (enable_optional_types_ &&
974
0
               call_expr.function() == kOptionalOrFn &&
975
0
               call_expr.has_target() && call_expr.args().size() == 1) {
976
0
      cond_visitor = std::make_unique<OptionalOrCondVisitor>(
977
0
          this, /*is_or_value=*/false, options_.short_circuiting);
978
112k
    } else if (enable_optional_types_ &&
979
0
               call_expr.function() == kOptionalOrValueFn &&
980
0
               call_expr.has_target() && call_expr.args().size() == 1) {
981
0
      cond_visitor = std::make_unique<OptionalOrCondVisitor>(
982
0
          this, /*is_or_value=*/true, options_.short_circuiting);
983
112k
    } else if (IsBlock(&call_expr)) {
984
      // cel.@block
985
0
      if (block_.has_value()) {
986
        // There can only be one for now.
987
0
        SetProgressStatusIfError(
988
0
            absl::InvalidArgumentError("multiple cel.@block are not allowed"));
989
0
        return;
990
0
      }
991
0
      block_ = BlockInfo();
992
0
      BlockInfo& block = *block_;
993
0
      block.in = true;
994
0
      if (call_expr.args().empty()) {
995
0
        SetProgressStatusIfError(absl::InvalidArgumentError(
996
0
            "malformed cel.@block: missing list of bound expressions"));
997
0
        return;
998
0
      }
999
0
      if (call_expr.args().size() != 2) {
1000
0
        SetProgressStatusIfError(absl::InvalidArgumentError(
1001
0
            "malformed cel.@block: missing bound expression"));
1002
0
        return;
1003
0
      }
1004
0
      if (!call_expr.args()[0].has_list_expr()) {
1005
0
        SetProgressStatusIfError(
1006
0
            absl::InvalidArgumentError("malformed cel.@block: first argument "
1007
0
                                       "is not a list of bound expressions"));
1008
0
        return;
1009
0
      }
1010
0
      const auto& list_expr = call_expr.args().front().list_expr();
1011
0
      block.size = list_expr.elements().size();
1012
1013
0
      block.bindings_set.reserve(block.size);
1014
0
      for (const auto& list_expr_element : list_expr.elements()) {
1015
0
        if (list_expr_element.optional()) {
1016
0
          SetProgressStatusIfError(
1017
0
              absl::InvalidArgumentError("malformed cel.@block: list of bound "
1018
0
                                         "expressions contains an optional"));
1019
0
          return;
1020
0
        }
1021
0
        block.bindings_set.insert(&list_expr_element.expr());
1022
0
      }
1023
0
      block.index = index_manager().ReserveSlots(block.size);
1024
0
      block.slot_count = block.size;
1025
0
      block.expr = &expr;
1026
0
      block.bindings = &call_expr.args()[0];
1027
0
      block.bound = &call_expr.args()[1];
1028
0
      block.subexpressions.resize(block.size, -1);
1029
112k
    } else {
1030
112k
      return;
1031
112k
    }
1032
1033
6.89k
    if (cond_visitor) {
1034
6.89k
      cond_visitor->PreVisit(&expr);
1035
6.89k
      cond_visitor_stack_.push({&expr, std::move(cond_visitor)});
1036
6.89k
    }
1037
6.89k
  }
1038
1039
  // Invoked after all child nodes are processed.
1040
  void PostVisitCall(const cel::Expr& expr,
1041
124k
                     const cel::CallExpr& call_expr) override {
1042
124k
    if (!progress_status_.ok()) {
1043
7.60k
      return;
1044
7.60k
    }
1045
1046
116k
    auto cond_visitor = FindCondVisitor(&expr);
1047
116k
    if (cond_visitor) {
1048
6.68k
      cond_visitor->PostVisit(&expr);
1049
6.68k
      cond_visitor_stack_.pop();
1050
6.68k
      return;
1051
6.68k
    }
1052
1053
    // Check if the call is intercepted by a custom handler.
1054
109k
    if (auto handler = call_handlers_.find(call_expr.function());
1055
109k
        handler != call_handlers_.end()) {
1056
36.7k
      CallHandlerResult result = handler->second(expr, call_expr);
1057
36.7k
      if (result == CallHandlerResult::kIntercepted) {
1058
15.5k
        return;
1059
15.5k
      }  // otherwise, apply default function handling.
1060
36.7k
    }
1061
1062
94.3k
    AddResolvedFunctionStep(&call_expr, &expr, call_expr.function());
1063
94.3k
  }
1064
1065
  void PreVisitComprehension(
1066
      const cel::Expr& expr,
1067
17.6k
      const cel::ComprehensionExpr& comprehension) override {
1068
17.6k
    if (!progress_status_.ok()) {
1069
520
      return;
1070
520
    }
1071
17.1k
    if (!ValidateOrError(options_.enable_comprehension,
1072
17.1k
                         "Comprehension support is disabled")) {
1073
0
      return;
1074
0
    }
1075
17.1k
    const auto& accu_var = comprehension.accu_var();
1076
17.1k
    const auto& iter_var = comprehension.iter_var();
1077
17.1k
    const auto& iter_var2 = comprehension.iter_var2();
1078
17.1k
    ValidateOrError(!accu_var.empty(),
1079
17.1k
                    "Invalid comprehension: 'accu_var' must not be empty");
1080
17.1k
    ValidateOrError(!iter_var.empty(),
1081
17.1k
                    "Invalid comprehension: 'iter_var' must not be empty");
1082
17.1k
    ValidateOrError(
1083
17.1k
        accu_var != iter_var,
1084
17.1k
        "Invalid comprehension: 'accu_var' must not be the same as 'iter_var'");
1085
17.1k
    ValidateOrError(accu_var != iter_var2,
1086
17.1k
                    "Invalid comprehension: 'accu_var' must not be the same as "
1087
17.1k
                    "'iter_var2'");
1088
17.1k
    ValidateOrError(iter_var2 != iter_var,
1089
17.1k
                    "Invalid comprehension: 'iter_var2' must not be the same "
1090
17.1k
                    "as 'iter_var'");
1091
17.1k
    ValidateOrError(comprehension.has_accu_init(),
1092
17.1k
                    "Invalid comprehension: 'accu_init' must be set");
1093
17.1k
    ValidateOrError(comprehension.has_loop_condition(),
1094
17.1k
                    "Invalid comprehension: 'loop_condition' must be set");
1095
17.1k
    ValidateOrError(comprehension.has_loop_step(),
1096
17.1k
                    "Invalid comprehension: 'loop_step' must be set");
1097
17.1k
    ValidateOrError(comprehension.has_result(),
1098
17.1k
                    "Invalid comprehension: 'result' must be set");
1099
1100
17.1k
    size_t iter_slot, iter2_slot, accu_slot, slot_count;
1101
17.1k
    bool is_bind = IsBind(&comprehension);
1102
1103
17.1k
    if (is_bind) {
1104
0
      accu_slot = iter_slot = iter2_slot = index_manager_.ReserveSlots(1);
1105
0
      slot_count = 1;
1106
17.1k
    } else if (comprehension.iter_var2().empty()) {
1107
17.1k
      iter_slot = iter2_slot = index_manager_.ReserveSlots(2);
1108
17.1k
      accu_slot = iter_slot + 1;
1109
17.1k
      slot_count = 2;
1110
17.1k
    } else {
1111
0
      iter_slot = index_manager_.ReserveSlots(3);
1112
0
      iter2_slot = iter_slot + 1;
1113
0
      accu_slot = iter2_slot + 1;
1114
0
      slot_count = 3;
1115
0
    }
1116
1117
17.1k
    if (block_.has_value()) {
1118
0
      BlockInfo& block = *block_;
1119
0
      if (block.in) {
1120
0
        block.slot_count += slot_count;
1121
0
        slot_count = 0;
1122
0
      }
1123
0
    }
1124
    // If this is in the scope of an optimized bind accu-init, account the slots
1125
    // to the outermost bind-init scope.
1126
    //
1127
    // The init expression is effectively inlined at the first usage in the
1128
    // critical path (which is unknown at plan time), so the used slots need to
1129
    // be dedicated for the entire scope of that bind.
1130
109k
    for (ComprehensionStackRecord& record : comprehension_stack_) {
1131
109k
      if (record.in_accu_init && record.is_optimizable_bind) {
1132
0
        record.slot_count += slot_count;
1133
0
        slot_count = 0;
1134
0
        break;
1135
0
      }
1136
      // If no bind init subexpression, account normally.
1137
109k
    }
1138
1139
17.1k
    comprehension_stack_.push_back(
1140
17.1k
        {&expr, &comprehension, iter_slot, iter2_slot, accu_slot, slot_count,
1141
17.1k
         /*subexpression=*/-1,
1142
         /*.is_optimizable_list_append=*/
1143
17.1k
         IsOptimizableListAppend(&comprehension,
1144
17.1k
                                 options_.enable_comprehension_list_append),
1145
         /*.is_optimizable_map_insert=*/
1146
17.1k
         IsOptimizableMapInsert(&comprehension,
1147
17.1k
                                options_.enable_comprehension_mutable_map),
1148
17.1k
         /*.is_optimizable_bind=*/is_bind,
1149
17.1k
         /*.iter_var_in_scope=*/false,
1150
17.1k
         /*.iter_var2_in_scope=*/false,
1151
17.1k
         /*.accu_var_in_scope=*/false,
1152
17.1k
         /*.in_accu_init=*/false,
1153
17.1k
         std::make_unique<ComprehensionVisitor>(this, is_bind, iter_slot,
1154
17.1k
                                                iter2_slot, accu_slot)});
1155
17.1k
    comprehension_stack_.back().visitor->PreVisit(&expr);
1156
17.1k
  }
1157
1158
  // Invoked after all child nodes are processed.
1159
  void PostVisitComprehension(
1160
      const cel::Expr& expr,
1161
17.6k
      const cel::ComprehensionExpr& comprehension_expr) override {
1162
17.6k
    if (!progress_status_.ok()) {
1163
2.48k
      return;
1164
2.48k
    }
1165
1166
15.1k
    ComprehensionStackRecord& record = comprehension_stack_.back();
1167
15.1k
    if (comprehension_stack_.empty() ||
1168
15.1k
        record.comprehension != &comprehension_expr) {
1169
0
      return;
1170
0
    }
1171
1172
15.1k
    record.visitor->PostVisit(&expr);
1173
1174
15.1k
    index_manager_.ReleaseSlots(record.slot_count);
1175
15.1k
    comprehension_stack_.pop_back();
1176
15.1k
  }
1177
1178
  void PreVisitComprehensionSubexpression(
1179
      const cel::Expr& expr, const cel::ComprehensionExpr& compr,
1180
88.1k
      cel::ComprehensionArg comprehension_arg) override {
1181
88.1k
    if (!progress_status_.ok()) {
1182
10.2k
      return;
1183
10.2k
    }
1184
1185
77.8k
    if (comprehension_stack_.empty() ||
1186
77.8k
        comprehension_stack_.back().comprehension != &compr) {
1187
0
      return;
1188
0
    }
1189
1190
77.8k
    ComprehensionStackRecord& record = comprehension_stack_.back();
1191
1192
77.8k
    switch (comprehension_arg) {
1193
17.1k
      case cel::ITER_RANGE: {
1194
17.1k
        record.in_accu_init = false;
1195
17.1k
        record.iter_var_in_scope = false;
1196
17.1k
        record.iter_var2_in_scope = false;
1197
17.1k
        record.accu_var_in_scope = false;
1198
17.1k
        break;
1199
0
      }
1200
15.2k
      case cel::ACCU_INIT: {
1201
15.2k
        record.in_accu_init = true;
1202
15.2k
        record.iter_var_in_scope = false;
1203
15.2k
        record.iter_var2_in_scope = false;
1204
15.2k
        record.accu_var_in_scope = false;
1205
15.2k
        break;
1206
0
      }
1207
15.2k
      case cel::LOOP_CONDITION: {
1208
15.2k
        record.in_accu_init = false;
1209
15.2k
        record.iter_var_in_scope = true;
1210
15.2k
        record.iter_var2_in_scope = true;
1211
15.2k
        record.accu_var_in_scope = true;
1212
15.2k
        break;
1213
0
      }
1214
15.2k
      case cel::LOOP_STEP: {
1215
15.2k
        record.in_accu_init = false;
1216
15.2k
        record.iter_var_in_scope = true;
1217
15.2k
        record.iter_var2_in_scope = true;
1218
15.2k
        record.accu_var_in_scope = true;
1219
15.2k
        break;
1220
0
      }
1221
15.1k
      case cel::RESULT: {
1222
15.1k
        record.in_accu_init = false;
1223
15.1k
        record.iter_var_in_scope = false;
1224
15.1k
        record.iter_var2_in_scope = false;
1225
15.1k
        record.accu_var_in_scope = true;
1226
15.1k
        break;
1227
0
      }
1228
77.8k
    }
1229
77.8k
  }
1230
1231
  void PostVisitComprehensionSubexpression(
1232
      const cel::Expr& expr, const cel::ComprehensionExpr& compr,
1233
88.1k
      cel::ComprehensionArg comprehension_arg) override {
1234
88.1k
    if (!progress_status_.ok()) {
1235
12.2k
      return;
1236
12.2k
    }
1237
1238
75.8k
    if (comprehension_stack_.empty() ||
1239
75.8k
        comprehension_stack_.back().comprehension != &compr) {
1240
0
      return;
1241
0
    }
1242
1243
75.8k
    SetProgressStatusIfError(comprehension_stack_.back().visitor->PostVisitArg(
1244
75.8k
        comprehension_arg, comprehension_stack_.back().expr));
1245
75.8k
  }
1246
1247
  // Invoked after each argument node processed.
1248
236k
  void PostVisitArg(const cel::Expr& expr, int arg_num) override {
1249
236k
    if (!progress_status_.ok()) {
1250
14.5k
      return;
1251
14.5k
    }
1252
221k
    auto cond_visitor = FindCondVisitor(&expr);
1253
221k
    if (cond_visitor) {
1254
16.0k
      cond_visitor->PostVisitArg(arg_num, &expr);
1255
16.0k
    }
1256
221k
  }
1257
1258
1.14k
  void PostVisitTarget(const cel::Expr& expr) override {
1259
1.14k
    if (!progress_status_.ok()) {
1260
833
      return;
1261
833
    }
1262
314
    auto cond_visitor = FindCondVisitor(&expr);
1263
314
    if (cond_visitor) {
1264
0
      cond_visitor->PostVisitTarget(&expr);
1265
0
    }
1266
314
  }
1267
1268
  // CreateList node handler.
1269
  // Invoked after child nodes are processed.
1270
  void PostVisitList(const cel::Expr& expr,
1271
55.7k
                     const cel::ListExpr& list_expr) override {
1272
55.7k
    if (!progress_status_.ok()) {
1273
5.10k
      return;
1274
5.10k
    }
1275
1276
50.6k
    if (block_.has_value()) {
1277
0
      BlockInfo& block = *block_;
1278
0
      if (block.bindings == &expr) {
1279
        // Do nothing, this is the cel.@block bindings list.
1280
0
        return;
1281
0
      }
1282
0
    }
1283
1284
50.6k
    if (!comprehension_stack_.empty()) {
1285
47.2k
      const ComprehensionStackRecord& comprehension =
1286
47.2k
          comprehension_stack_.back();
1287
47.2k
      if (comprehension.is_optimizable_list_append) {
1288
0
        if (&(comprehension.comprehension->accu_init()) == &expr) {
1289
0
          AddStep(ExpressionStep::MakeNewMutableListStep(expr.id()));
1290
0
          return;
1291
0
        }
1292
0
        if (GetOptimizableListAppendOperand(comprehension.comprehension) ==
1293
0
            &expr) {
1294
          // Avoid wrapping the appended element in a create list step.
1295
0
          return;
1296
0
        }
1297
0
      }
1298
47.2k
    }
1299
50.6k
    AddStep(CreateCreateListStep(list_expr), expr.id());
1300
50.6k
  }
1301
1302
  // CreateStruct node handler.
1303
  // Invoked after child nodes are processed.
1304
  void PostVisitStruct(const cel::Expr& expr,
1305
3.38k
                       const cel::StructExpr& struct_expr) override {
1306
3.38k
    if (!progress_status_.ok()) {
1307
529
      return;
1308
529
    }
1309
1310
2.85k
    auto status_or_resolved_fields =
1311
2.85k
        ResolveCreateStructFields(struct_expr, expr.id());
1312
2.85k
    if (!status_or_resolved_fields.ok()) {
1313
286
      SetProgressStatusIfError(status_or_resolved_fields.status());
1314
286
      return;
1315
286
    }
1316
2.56k
    std::string resolved_name =
1317
2.56k
        std::move(status_or_resolved_fields.value().first);
1318
2.56k
    std::vector<std::string> fields =
1319
2.56k
        std::move(status_or_resolved_fields.value().second);
1320
1321
2.56k
    AddStep(CreateCreateStructStep(std::move(resolved_name), std::move(fields),
1322
2.56k
                                   MakeOptionalIndicesSet(struct_expr)),
1323
2.56k
            expr.id());
1324
2.56k
  }
1325
1326
  void PostVisitMap(const cel::Expr& expr,
1327
4.60k
                    const cel::MapExpr& map_expr) override {
1328
5.10k
    for (const auto& entry : map_expr.entries()) {
1329
5.10k
      ValidateOrError(entry.has_key(), "Map entry missing key");
1330
5.10k
      ValidateOrError(entry.has_value(), "Map entry missing value");
1331
5.10k
    }
1332
1333
4.60k
    if (!comprehension_stack_.empty()) {
1334
2.75k
      const ComprehensionStackRecord& comprehension =
1335
2.75k
          comprehension_stack_.back();
1336
2.75k
      if (comprehension.is_optimizable_map_insert) {
1337
0
        if (&(comprehension.comprehension->accu_init()) == &expr) {
1338
0
          AddStep(CreateMutableMapStep(), expr.id());
1339
0
          return;
1340
0
        }
1341
0
      }
1342
2.75k
    }
1343
1344
4.60k
    AddStep(CreateCreateStructStepForMap(map_expr.entries().size(),
1345
4.60k
                                         MakeOptionalIndicesSet(map_expr)),
1346
4.60k
            expr.id());
1347
4.60k
  }
1348
1349
18.4k
  absl::Status progress_status() const { return progress_status_; }
1350
1351
  // Mark a branch as suppressed. The visitor will continue as normal, but
1352
  // any emitted program steps are ignored.
1353
  //
1354
  // Only applies to branches that have not yet been visited (pre-order).
1355
0
  void SuppressBranch(const cel::Expr* expr) {
1356
0
    suppressed_branches_.insert(expr);
1357
0
  }
1358
1359
  void AddResolvedFunctionStep(const cel::CallExpr* call_expr,
1360
                               const cel::Expr* expr,
1361
94.3k
                               absl::string_view function) {
1362
    // Establish the search criteria for a given function.
1363
94.3k
    bool receiver_style = call_expr->has_target();
1364
94.3k
    size_t num_args = call_expr->args().size() + (receiver_style ? 1 : 0);
1365
1366
    // First, search for lazily defined function overloads.
1367
    // Lazy functions shadow eager functions with the same signature.
1368
94.3k
    auto lazy_overloads = resolver_.FindLazyOverloads(
1369
94.3k
        function, call_expr->has_target(), num_args, expr->id());
1370
94.3k
    if (!lazy_overloads.empty()) {
1371
0
      AddStep(ExpressionStep::MakeLazyFunctionStep(
1372
0
          CreateLazyFunctionStep(*call_expr, expr->id(),
1373
0
                                 std::move(lazy_overloads)),
1374
0
          expr->id()));
1375
0
      return;
1376
0
    }
1377
1378
    // Second, search for eagerly defined function overloads.
1379
94.3k
    auto overloads =
1380
94.3k
        resolver_.FindOverloads(function, receiver_style, num_args, expr->id());
1381
94.3k
    if (overloads.empty()) {
1382
      // Create a warning that the overload could not be found. Depending on the
1383
      // builder_warnings configuration, this could result in termination of the
1384
      // CelExpression creation or an inspectable warning for use within runtime
1385
      // logging.
1386
292
      auto status = issue_collector_.AddIssue(RuntimeIssue::CreateWarning(
1387
292
          absl::InvalidArgumentError(
1388
292
              "No overloads provided for FunctionStep creation"),
1389
292
          RuntimeIssue::ErrorCode::kNoMatchingOverload));
1390
292
      if (!status.ok()) {
1391
292
        SetProgressStatusIfError(status);
1392
292
        return;
1393
292
      }
1394
292
    }
1395
1396
94.0k
    AddStep(ExpressionStep::MakeEagerFunctionStep(
1397
94.0k
        CreateFunctionStep(*call_expr, expr->id(), std::move(overloads)),
1398
94.0k
        expr->id()));
1399
94.0k
  }
1400
1401
  // Add a step to the program, taking ownership. If successful, returns the
1402
  // pointer to the step. Otherwise, returns nullptr.
1403
  //
1404
  // Note: the pointer is only guaranteed to stay valid until the parent
1405
  // subexpression is finalized. Optimizers may modify the program plan which
1406
  // may free the step at that point.
1407
  template <typename T>
1408
  std::enable_if_t<std::is_base_of_v<ExpressionStepLogic, T>, T*> AddStep(
1409
92.9k
      std::unique_ptr<T> step, int64_t expr_id = -1) {
1410
92.9k
    if (progress_status_.ok() && !PlanningSuppressed()) {
1411
92.5k
      T* ptr = step.get();
1412
92.5k
      program_builder_.AddStep(
1413
92.5k
          ExpressionStep::MakeGenericStep(std::move(step), expr_id));
1414
92.5k
      return ptr;
1415
92.5k
    }
1416
400
    return nullptr;
1417
92.9k
  }
flat_expr_builder.cc:_ZN6google3api4expr7runtime12_GLOBAL__N_115FlatExprVisitor7AddStepINS2_19ExpressionStepLogicEEENSt3__19enable_ifIXsr3stdE12is_base_of_vIS6_T_EEPS9_E4typeENS7_10unique_ptrIS9_NS7_14default_deleteIS9_EEEEl
Line
Count
Source
1409
77.7k
      std::unique_ptr<T> step, int64_t expr_id = -1) {
1410
77.7k
    if (progress_status_.ok() && !PlanningSuppressed()) {
1411
77.3k
      T* ptr = step.get();
1412
77.3k
      program_builder_.AddStep(
1413
77.3k
          ExpressionStep::MakeGenericStep(std::move(step), expr_id));
1414
77.3k
      return ptr;
1415
77.3k
    }
1416
400
    return nullptr;
1417
77.7k
  }
Unexecuted instantiation: flat_expr_builder.cc:_ZN6google3api4expr7runtime12_GLOBAL__N_115FlatExprVisitor7AddStepINS2_24OptionalHasValueJumpStepEEENSt3__19enable_ifIXsr3stdE12is_base_of_vINS2_19ExpressionStepLogicET_EEPSA_E4typeENS7_10unique_ptrISA_NS7_14default_deleteISA_EEEEl
flat_expr_builder.cc:_ZN6google3api4expr7runtime12_GLOBAL__N_115FlatExprVisitor7AddStepINS2_21ComprehensionInitStepEEENSt3__19enable_ifIXsr3stdE12is_base_of_vINS2_19ExpressionStepLogicET_EEPSA_E4typeENS7_10unique_ptrISA_NS7_14default_deleteISA_EEEEl
Line
Count
Source
1409
15.2k
      std::unique_ptr<T> step, int64_t expr_id = -1) {
1410
15.2k
    if (progress_status_.ok() && !PlanningSuppressed()) {
1411
15.2k
      T* ptr = step.get();
1412
15.2k
      program_builder_.AddStep(
1413
15.2k
          ExpressionStep::MakeGenericStep(std::move(step), expr_id));
1414
15.2k
      return ptr;
1415
15.2k
    }
1416
0
    return nullptr;
1417
15.2k
  }
1418
1419
  template <typename T>
1420
  std::enable_if_t<std::is_base_of_v<ExpressionStepLogic, T>, T*> AddStep(
1421
75.1k
      absl::StatusOr<std::unique_ptr<T>> step, int64_t expr_id = -1) {
1422
75.1k
    if (step.ok()) {
1423
75.1k
      return AddStep(*std::move(step), expr_id);
1424
75.1k
    } else {
1425
0
      SetProgressStatusIfError(step.status());
1426
0
    }
1427
0
    return nullptr;
1428
75.1k
  }
1429
1430
427k
  ExpressionStep* AddStep(ExpressionStep step) {
1431
427k
    if (progress_status_.ok() && !PlanningSuppressed()) {
1432
427k
      return program_builder_.AddStep(std::move(step));
1433
427k
    }
1434
0
    return nullptr;
1435
427k
  }
1436
1437
76.4k
  void SetProgressStatusIfError(const absl::Status& status) {
1438
76.4k
    if (progress_status_.ok() && !status.ok()) {
1439
587
      progress_status_ = status;
1440
587
    }
1441
76.4k
  }
1442
1443
  // Index of the next step to be inserted, in terms of the current
1444
  // subexpression
1445
157k
  ProgramStepIndex GetCurrentIndex() const {
1446
    // Nonnull while active -- nullptr indicates logic error in the builder.
1447
157k
    ABSL_DCHECK(program_builder_.current() != nullptr);
1448
157k
    return {static_cast<int>(program_builder_.current()->elements().size()),
1449
157k
            program_builder_.current()};
1450
157k
  }
1451
1452
338k
  CondVisitor* FindCondVisitor(const cel::Expr* expr) const {
1453
338k
    if (cond_visitor_stack_.empty()) {
1454
242k
      return nullptr;
1455
242k
    }
1456
1457
95.9k
    const auto& latest = cond_visitor_stack_.top();
1458
1459
95.9k
    return (latest.first == expr) ? latest.second.get() : nullptr;
1460
338k
  }
1461
1462
0
  IndexManager& index_manager() { return index_manager_; }
1463
1464
34.6k
  size_t slot_count() const { return index_manager_.max_slot_count(); }
1465
1466
0
  void AddOptimizer(std::unique_ptr<ProgramOptimizer> optimizer) {
1467
0
    program_optimizers_.push_back(std::move(optimizer));
1468
0
  }
1469
1470
  // Tests the boolean predicate, and if false produces an InvalidArgumentError
1471
  // which concatenates the error_message and any optional message_parts as the
1472
  // error status message.
1473
  template <typename... MP>
1474
  bool ValidateOrError(bool valid_expression, absl::string_view error_message,
1475
818k
                       MP... message_parts) {
1476
818k
    if (valid_expression) {
1477
818k
      return true;
1478
818k
    }
1479
9
    SetProgressStatusIfError(absl::InvalidArgumentError(
1480
9
        absl::StrCat(error_message, message_parts...)));
1481
9
    return false;
1482
818k
  }
1483
1484
 private:
1485
  struct ComprehensionStackRecord {
1486
    const cel::Expr* expr;
1487
    const cel::ComprehensionExpr* comprehension;
1488
    size_t iter_slot;
1489
    size_t iter2_slot;
1490
    size_t accu_slot;
1491
    size_t slot_count;
1492
    // -1 indicates this shouldn't be used.
1493
    int subexpression;
1494
    bool is_optimizable_list_append;
1495
    bool is_optimizable_map_insert;
1496
    bool is_optimizable_bind;
1497
    bool iter_var_in_scope;
1498
    bool iter_var2_in_scope;
1499
    bool accu_var_in_scope;
1500
    bool in_accu_init;
1501
    std::unique_ptr<ComprehensionVisitor> visitor;
1502
  };
1503
1504
  struct BlockInfo {
1505
    // True if we are currently visiting the `cel.@block` node or any of its
1506
    // children.
1507
    bool in = false;
1508
    // Pointer to the `cel.@block` node.
1509
    const cel::Expr* expr = nullptr;
1510
    // Pointer to the `cel.@block` bindings, that is the first argument to the
1511
    // function.
1512
    const cel::Expr* bindings = nullptr;
1513
    // Set of pointers to the elements of `bindings` above.
1514
    absl::flat_hash_set<const cel::Expr*> bindings_set;
1515
    // Pointer to the `cel.@block` bound expression, that is the second argument
1516
    // to the function.
1517
    const cel::Expr* bound = nullptr;
1518
    // The number of entries in the `cel.@block`.
1519
    size_t size = 0;
1520
    // Starting slot index for `cel.@block`. We occupy he slot indices `index`
1521
    // through `index + size + (var_size * 2)`.
1522
    size_t index = 0;
1523
    // The total number of slots needed for evaluating the bound expressions.
1524
    size_t slot_count = 0;
1525
    // The current slot index we are processing, any index references must be
1526
    // less than this to be valid.
1527
    size_t current_index = 0;
1528
    // Pointer to the current `cel.@block` being processed, that is one of the
1529
    // elements within the first argument.
1530
    const cel::Expr* current_binding = nullptr;
1531
    // Mapping between block indices and their subexpressions, fixed size with
1532
    // exactly `size` elements. Unprocessed indices are set to `-1`.
1533
    std::vector<int> subexpressions;
1534
  };
1535
1536
520k
  bool PlanningSuppressed() const {
1537
520k
    return resume_from_suppressed_branch_ != nullptr;
1538
520k
  }
1539
1540
  absl::Status MaybeExtractSubexpression(const cel::Expr* expr,
1541
0
                                         ComprehensionStackRecord& record) {
1542
0
    if (!record.is_optimizable_bind) {
1543
0
      return absl::OkStatus();
1544
0
    }
1545
1546
0
    int index = program_builder_.ExtractSubexpression(expr);
1547
0
    if (index == -1) {
1548
0
      return absl::InternalError("Failed to extract subexpression");
1549
0
    }
1550
1551
0
    record.subexpression = index;
1552
1553
0
    record.visitor->MarkAccuInitExtracted();
1554
1555
0
    return absl::OkStatus();
1556
0
  }
1557
1558
  // Resolve the name of the message type being created and the names of set
1559
  // fields.
1560
  absl::StatusOr<std::pair<std::string, std::vector<std::string>>>
1561
  ResolveCreateStructFields(const cel::StructExpr& create_struct_expr,
1562
2.85k
                            int64_t expr_id) {
1563
2.85k
    absl::string_view ast_name = create_struct_expr.name();
1564
1565
2.85k
    std::optional<std::pair<std::string, cel::Type>> type;
1566
2.85k
    CEL_ASSIGN_OR_RETURN(type, resolver_.FindType(ast_name, expr_id));
1567
1568
2.85k
    if (!type.has_value()) {
1569
241
      return absl::InvalidArgumentError(absl::StrCat(
1570
241
          "Invalid struct creation: missing type info for '", ast_name, "'"));
1571
241
    }
1572
1573
2.61k
    std::string resolved_name = std::move(type).value().first;
1574
1575
2.61k
    std::vector<std::string> fields;
1576
2.61k
    fields.reserve(create_struct_expr.fields().size());
1577
2.61k
    for (const auto& entry : create_struct_expr.fields()) {
1578
1.24k
      if (entry.name().empty()) {
1579
0
        return absl::InvalidArgumentError("Struct field missing name");
1580
0
      }
1581
1.24k
      if (!entry.has_value()) {
1582
0
        return absl::InvalidArgumentError("Struct field missing value");
1583
0
      }
1584
2.48k
      CEL_ASSIGN_OR_RETURN(auto field, type_provider_.FindStructTypeFieldByName(
1585
2.48k
                                           resolved_name, entry.name()));
1586
2.48k
      if (!field.has_value()) {
1587
45
        return absl::InvalidArgumentError(
1588
45
            absl::StrCat("Invalid message creation: field '", entry.name(),
1589
45
                         "' not found in '", resolved_name, "'"));
1590
45
      }
1591
1.19k
      fields.push_back(entry.name());
1592
1.19k
    }
1593
1594
2.56k
    return std::make_pair(std::move(resolved_name), std::move(fields));
1595
2.61k
  }
1596
1597
  CallHandlerResult HandleIndex(const cel::Expr& expr,
1598
                                const cel::CallExpr& call);
1599
  CallHandlerResult HandleBlock(const cel::Expr& expr,
1600
                                const cel::CallExpr& call);
1601
  CallHandlerResult HandleListAppend(const cel::Expr& expr,
1602
                                     const cel::CallExpr& call);
1603
  CallHandlerResult HandleNot(const cel::Expr& expr, const cel::CallExpr& call);
1604
  CallHandlerResult HandleNotStrictlyFalse(const cel::Expr& expr,
1605
                                           const cel::CallExpr& call);
1606
1607
  CallHandlerResult HandleHeterogeneousEquality(const cel::Expr& expr,
1608
                                                const cel::CallExpr& call,
1609
                                                bool inequality);
1610
1611
  CallHandlerResult HandleHeterogeneousEqualityIn(const cel::Expr& expr,
1612
                                                  const cel::CallExpr& call);
1613
1614
  void MaybeResolveType(const cel::Expr& expr);
1615
1616
  const Resolver& resolver_;
1617
  const cel::TypeProvider& type_provider_;
1618
  absl::Status progress_status_;
1619
  absl::flat_hash_map<std::string, CallHandler> call_handlers_;
1620
1621
  std::stack<std::pair<const cel::Expr*, std::unique_ptr<CondVisitor>>>
1622
      cond_visitor_stack_;
1623
1624
  // Tracks SELECT-...SELECT-IDENT chains.
1625
  std::deque<std::pair<const cel::Expr*, std::string>> namespace_stack_;
1626
1627
  // When multiple SELECT-...SELECT-IDENT chain is resolved as namespace, this
1628
  // field is used as marker suppressing CelExpression creation for SELECTs.
1629
  const cel::Expr* resolved_select_expr_;
1630
1631
  const cel::RuntimeOptions& options_;
1632
1633
  std::vector<ComprehensionStackRecord> comprehension_stack_;
1634
  absl::flat_hash_set<const cel::Expr*> suppressed_branches_;
1635
  const cel::Expr* resume_from_suppressed_branch_ = nullptr;
1636
  std::vector<std::unique_ptr<ProgramOptimizer>> program_optimizers_;
1637
  const absl::flat_hash_map<int64_t, cel::TypeSpec>& type_map_;
1638
  absl::flat_hash_map<const cel::Expr*, cel::Type> resolved_types_;
1639
  IssueCollector& issue_collector_;
1640
1641
  ProgramBuilder& program_builder_;
1642
  PlannerContext& extension_context_;
1643
  IndexManager index_manager_;
1644
1645
  bool enable_optional_types_;
1646
  std::optional<FlatExprVisitor::BlockInfo> block_;
1647
};
1648
1649
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleIndex(
1650
3.02k
    const cel::Expr& expr, const cel::CallExpr& call_expr) {
1651
3.02k
  ABSL_DCHECK(call_expr.function() == cel::builtin::kIndex);
1652
3.02k
  if (!ValidateOrError(
1653
3.02k
          (call_expr.args().size() == 2 && !call_expr.has_target()) ||
1654
              // TODO(uncreated-issue/79): A few clients use the index operator with a
1655
              // target in custom ASTs.
1656
0
              (call_expr.args().size() == 1 && call_expr.has_target()),
1657
3.02k
          "unexpected number of args for builtin index operator")) {
1658
0
    return CallHandlerResult::kIntercepted;
1659
0
  }
1660
1661
3.02k
  AddStep(CreateContainerAccessStep(call_expr, enable_optional_types_),
1662
3.02k
          expr.id());
1663
3.02k
  return CallHandlerResult::kIntercepted;
1664
3.02k
}
1665
1666
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleNot(
1667
638
    const cel::Expr& expr, const cel::CallExpr& call_expr) {
1668
638
  ABSL_DCHECK(call_expr.function() == cel::builtin::kNot);
1669
1670
638
  if (!ValidateOrError(call_expr.args().size() == 1 && !call_expr.has_target(),
1671
638
                       "unexpected number of args for builtin not operator")) {
1672
0
    return CallHandlerResult::kIntercepted;
1673
0
  }
1674
1675
638
  AddStep(ExpressionStep::MakeBooleanNotStep(expr.id()));
1676
638
  return CallHandlerResult::kIntercepted;
1677
638
}
1678
1679
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleNotStrictlyFalse(
1680
154
    const cel::Expr& expr, const cel::CallExpr& call_expr) {
1681
154
  if (!ValidateOrError(call_expr.args().size() == 1 && !call_expr.has_target(),
1682
154
                       "unexpected number of args for builtin "
1683
154
                       "not_strictly_false operator")) {
1684
0
    return CallHandlerResult::kIntercepted;
1685
0
  }
1686
1687
154
  AddStep(ExpressionStep::MakeNotStrictlyFalseStep(expr.id()));
1688
154
  return CallHandlerResult::kIntercepted;
1689
154
}
1690
1691
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleBlock(
1692
0
    const cel::Expr& expr, const cel::CallExpr& call_expr) {
1693
0
  ABSL_DCHECK(call_expr.function() == kBlock);
1694
0
  if (!block_.has_value() || block_->expr != &expr ||
1695
0
      call_expr.args().size() != 2 || call_expr.has_target()) {
1696
0
    SetProgressStatusIfError(
1697
0
        absl::InvalidArgumentError("unexpected call to internal cel.@block"));
1698
0
    return CallHandlerResult::kIntercepted;
1699
0
  }
1700
1701
0
  BlockInfo& block = *block_;
1702
0
  block.in = false;
1703
0
  index_manager().ReleaseSlots(block.slot_count);
1704
1705
  // Otherwise, iterative plan.
1706
0
  if (block.slot_count > 0) {
1707
0
    AddStep(ExpressionStep::MakeClearSlotsStep(block.index, block.slot_count,
1708
0
                                               expr.id()));
1709
0
  }
1710
1711
0
  return CallHandlerResult::kIntercepted;
1712
0
}
1713
1714
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleListAppend(
1715
21.1k
    const cel::Expr& expr, const cel::CallExpr& call_expr) {
1716
21.1k
  ABSL_DCHECK(call_expr.function() == cel::builtin::kAdd);
1717
1718
  // Check to see if this is a special case of add that should really be
1719
  // treated as a list append
1720
21.1k
  if (!comprehension_stack_.empty() &&
1721
17.8k
      comprehension_stack_.back().is_optimizable_list_append) {
1722
    // Already checked that this is an optimizeable comprehension,
1723
    // check that this is the correct list append node.
1724
0
    const cel::ComprehensionExpr* comprehension =
1725
0
        comprehension_stack_.back().comprehension;
1726
0
    const cel::Expr& loop_step = comprehension->loop_step();
1727
1728
    // Macro loop_step for a map() will contain a list concat operation:
1729
    //   accu_var + [elem]
1730
0
    const bool is_map_loop_step = &loop_step == &expr;
1731
1732
    // Macro loop_step for a filter() will contain a ternary:
1733
    //   filter ? accu_var + [elem] : accu_var
1734
0
    const bool is_filter_loop_step =
1735
0
        loop_step.has_call_expr() &&
1736
0
        loop_step.call_expr().function() == cel::builtin::kTernary &&
1737
0
        loop_step.call_expr().args().size() == 3 &&
1738
0
        &(loop_step.call_expr().args()[1]) == &expr;
1739
0
    if (is_map_loop_step || is_filter_loop_step) {
1740
0
        AddStep(ExpressionStep::MakeMutableListAppendStep(expr.id()));
1741
0
      return CallHandlerResult::kIntercepted;
1742
0
    }
1743
0
  }
1744
1745
21.1k
  return CallHandlerResult::kNotIntercepted;
1746
21.1k
}
1747
1748
FlatExprVisitor::CallHandlerResult FlatExprVisitor::HandleHeterogeneousEquality(
1749
9.98k
    const cel::Expr& expr, const cel::CallExpr& call, bool inequality) {
1750
9.98k
  if (!ValidateOrError(
1751
9.98k
          call.args().size() == 2 && !call.has_target(),
1752
9.98k
          "unexpected number of args for builtin equality operator")) {
1753
0
    return CallHandlerResult::kIntercepted;
1754
0
  }
1755
1756
9.98k
  AddStep(inequality ? ExpressionStep::MakeFastNotEqualStep(expr.id())
1757
9.98k
                     : ExpressionStep::MakeFastEqualStep(expr.id()));
1758
9.98k
  return CallHandlerResult::kIntercepted;
1759
9.98k
}
1760
1761
FlatExprVisitor::CallHandlerResult
1762
FlatExprVisitor::HandleHeterogeneousEqualityIn(const cel::Expr& expr,
1763
1.78k
                                               const cel::CallExpr& call) {
1764
1.78k
  if (!ValidateOrError(call.args().size() == 2 && !call.has_target(),
1765
1.78k
                       "unexpected number of args for builtin 'in' operator")) {
1766
9
    return CallHandlerResult::kIntercepted;
1767
9
  }
1768
1769
1.77k
  AddStep(ExpressionStep::MakeFastInStep(expr.id()));
1770
1.77k
  return CallHandlerResult::kIntercepted;
1771
1.78k
}
1772
1773
457k
void FlatExprVisitor::MaybeResolveType(const cel::Expr& expr) {
1774
  // Try to resolve the type from the type map, but don't fail if it's not
1775
  // there. This permits cases where the runtime type is compatible but not
1776
  // the same as the type checked type.
1777
457k
  auto it = type_map_.find(expr.id());
1778
457k
  if (it == type_map_.end()) {
1779
457k
    return;
1780
457k
  }
1781
0
  absl::StatusOr<cel::Type> type = cel::ConvertTypeSpecToType(
1782
0
      it->second, extension_context_.type_reflector(),
1783
0
      extension_context_.MutableArena());
1784
0
  if (!type.ok()) {
1785
0
    return;
1786
0
  }
1787
0
  SetResolvedType(expr, *type);
1788
0
}
1789
1790
4.42k
void LogicalCondVisitor::PreVisit(const cel::Expr* expr) {
1791
4.42k
  visitor_->ValidateOrError(
1792
4.42k
      !expr->call_expr().has_target() && expr->call_expr().args().size() >= 2,
1793
4.42k
      "Invalid argument count for a binary function call.");
1794
4.42k
}
1795
1796
8.75k
void LogicalCondVisitor::PostVisitArg(int arg_num, const cel::Expr* expr) {
1797
8.75k
  const int last_arg_index = expr->call_expr().args().size() - 1;
1798
8.75k
  const size_t num_args = expr->call_expr().args().size();
1799
8.75k
  if (arg_num == last_arg_index) {
1800
4.37k
    if (is_or_) {
1801
2.67k
      visitor_->AddStep(
1802
2.67k
          ExpressionStep::MakeBooleanOrStep(num_args, expr->id()));
1803
2.67k
    } else {
1804
1.70k
      visitor_->AddStep(
1805
1.70k
          ExpressionStep::MakeBooleanAndStep(num_args, expr->id()));
1806
1.70k
    }
1807
4.37k
    if (short_circuiting_ && !jump_steps_.empty()) {
1808
4.37k
      ProgramStepIndex target = visitor_->GetCurrentIndex();
1809
4.37k
      for (const ProgramStepIndex& jump_pos : jump_steps_) {
1810
4.37k
        absl::StatusOr<int> offset = Jump::CalculateOffset(jump_pos, target);
1811
4.37k
        if (!offset.ok()) {
1812
0
          visitor_->SetProgressStatusIfError(offset.status());
1813
0
          continue;
1814
0
        }
1815
4.37k
        ExpressionStep* step =
1816
4.37k
            jump_pos.subexpression->GetIfExpressionStep(jump_pos.index);
1817
4.37k
        BoolJumpStepInfo* jump_info = GetIfBoolJumpStep(step);
1818
4.37k
        if (jump_info != nullptr) {
1819
4.37k
          jump_info->set = true;
1820
4.37k
          jump_info->offset = *offset;
1821
4.37k
        }
1822
4.37k
      }
1823
4.37k
    }
1824
4.37k
  }
1825
8.75k
  if (short_circuiting_ && arg_num < last_arg_index) {
1826
4.38k
    ProgramStepIndex index = visitor_->GetCurrentIndex();
1827
4.38k
    visitor_->AddStep(is_or_ ? ExpressionStep::MakeBooleanOrJumpStep(
1828
2.67k
                                   /*arg_count=*/arg_num + 1)
1829
4.38k
                             : ExpressionStep::MakeBooleanAndJumpStep(
1830
1.70k
                                   /*arg_count=*/arg_num + 1));
1831
4.38k
    jump_steps_.push_back(index);
1832
4.38k
  }
1833
8.75k
}
1834
1835
4.37k
void LogicalCondVisitor::PostVisit(const cel::Expr* expr) {
1836
4.37k
}
1837
1838
0
void OptionalOrCondVisitor::PreVisit(const cel::Expr* expr) {
1839
0
  visitor_->ValidateOrError(
1840
0
      expr->call_expr().has_target() && expr->call_expr().args().size() == 1,
1841
0
      "Invalid argument count for or/orValue call.");
1842
0
}
1843
1844
0
void OptionalOrCondVisitor::PostVisitTarget(const cel::Expr* expr) {
1845
0
  if (!short_circuiting_) {
1846
0
    return;
1847
0
  }
1848
1849
  // Keep a pointer to the jump step so we can update the target after
1850
  // planning the second argument.
1851
0
  jump_step_pos_ = visitor_->GetCurrentIndex();
1852
0
  jump_step_ = visitor_->AddStep(CreateOptionalHasValueJumpStep(is_or_value_));
1853
0
}
1854
1855
0
void OptionalOrCondVisitor::PostVisit(const cel::Expr* expr) {
1856
0
  visitor_->AddStep(CreateOptionalOrStep(is_or_value_), expr->id());
1857
0
  if (!short_circuiting_) {
1858
0
    return;
1859
0
  }
1860
1861
0
  ABSL_DCHECK(jump_step_ != nullptr)
1862
0
      << "OptionalOrCondVisitor::PostVisit: jump_step_ is null";
1863
0
  absl::StatusOr<int> offset =
1864
0
      Jump::CalculateOffset(jump_step_pos_, visitor_->GetCurrentIndex());
1865
0
  if (!offset.ok()) {
1866
0
    visitor_->SetProgressStatusIfError(offset.status());
1867
0
    return;
1868
0
  }
1869
1870
0
  jump_step_->set_jump_offset(*offset);
1871
0
}
1872
1873
2.47k
void TernaryCondVisitor::PreVisit(const cel::Expr* expr) {
1874
2.47k
  visitor_->ValidateOrError(
1875
2.47k
      !expr->call_expr().has_target() && expr->call_expr().args().size() == 3,
1876
2.47k
      "Invalid argument count for a ternary function call.");
1877
2.47k
}
1878
1879
7.24k
void TernaryCondVisitor::PostVisitArg(int arg_num, const cel::Expr* expr) {
1880
  // Ternary operator "_?_:_" requires a special handing.
1881
  // In contrary to regular function call, its execution affects the control
1882
  // flow of the overall CEL expression.
1883
  // If condition value (argument 0) is True, then control flow is unaffected
1884
  // as it is passed to the first conditional branch. Then, at the end of this
1885
  // branch, the jump is performed over the second conditional branch.
1886
  // If condition value is False, then jump is performed and control is passed
1887
  // to the beginning of the second conditional branch.
1888
  // If condition value is Error, then jump is peformed to bypass both
1889
  // conditional branches and provide Error as result of ternary operation.
1890
1891
  // condition argument for ternary operator
1892
7.24k
  if (arg_num == 0) {
1893
    // Jump in case of error or non-bool, or jump to the second branch of
1894
    // execution if false.
1895
2.47k
    cond_jump_pos_ = visitor_->GetCurrentIndex();
1896
2.47k
    visitor_->AddStep(ExpressionStep::MakeTernaryJumpStep());
1897
4.77k
  } else if (arg_num == 1) {
1898
    // Jump after the first and over the second branch of execution.
1899
2.46k
    jump_after_first_pos_ = visitor_->GetCurrentIndex();
1900
2.46k
    visitor_->AddStep(ExpressionStep::MakeFixedJumpStep());
1901
1902
2.46k
    ExpressionStep* cond_step =
1903
2.46k
        cond_jump_pos_.has_value()
1904
2.46k
            ? cond_jump_pos_->subexpression->GetIfExpressionStep(
1905
2.46k
                  cond_jump_pos_->index)
1906
2.46k
            : nullptr;
1907
2.46k
    TernaryJumpStepInfo* ternary_info = GetIfTernaryJumpStep(cond_step);
1908
2.46k
    if (!visitor_->ValidateOrError(
1909
2.46k
            ternary_info != nullptr,
1910
2.46k
            "Error configuring ternary operator: jump_to_second_ is null")) {
1911
0
      return;
1912
0
    }
1913
1914
2.46k
    absl::StatusOr<int> offset =
1915
2.46k
        Jump::CalculateOffset(*cond_jump_pos_, visitor_->GetCurrentIndex());
1916
2.46k
    if (!offset.ok()) {
1917
0
      visitor_->SetProgressStatusIfError(offset.status());
1918
0
      return;
1919
0
    }
1920
1921
2.46k
    ternary_info->jump_to_second_offset = *offset;
1922
2.46k
  }
1923
  // Code executed after traversing the final branch of execution
1924
  // (arg_num == 2) is placed in PostVisitCall, to make this method less
1925
  // clattered.
1926
7.24k
}
1927
1928
2.31k
void TernaryCondVisitor::PostVisit(const cel::Expr* expr) {
1929
  // Determine and set jump offset in jump instruction.
1930
2.31k
  ExpressionStep* cond_step =
1931
2.31k
      cond_jump_pos_.has_value()
1932
2.31k
          ? cond_jump_pos_->subexpression->GetIfExpressionStep(
1933
2.31k
                cond_jump_pos_->index)
1934
2.31k
          : nullptr;
1935
2.31k
  TernaryJumpStepInfo* ternary_info = GetIfTernaryJumpStep(cond_step);
1936
2.31k
  if (visitor_->ValidateOrError(
1937
2.31k
          ternary_info != nullptr,
1938
2.31k
          "Error configuring ternary operator: error_jump_ is null")) {
1939
2.31k
    absl::StatusOr<int> offset =
1940
2.31k
        Jump::CalculateOffset(*cond_jump_pos_, visitor_->GetCurrentIndex());
1941
2.31k
    if (!offset.ok()) {
1942
0
      visitor_->SetProgressStatusIfError(offset.status());
1943
2.31k
    } else {
1944
2.31k
      ternary_info->set = true;
1945
2.31k
      ternary_info->error_offset = *offset;
1946
2.31k
    }
1947
2.31k
  }
1948
2.31k
  ExpressionStep* jump_after_first_step =
1949
2.31k
      jump_after_first_pos_.has_value()
1950
2.31k
          ? jump_after_first_pos_->subexpression->GetIfExpressionStep(
1951
2.31k
                jump_after_first_pos_->index)
1952
2.31k
          : nullptr;
1953
2.31k
  FixedJumpStepInfo* fixed_info = GetIfFixedJumpStep(jump_after_first_step);
1954
2.31k
  if (visitor_->ValidateOrError(
1955
2.31k
          fixed_info != nullptr,
1956
2.31k
          "Error configuring ternary operator: jump_after_first_ is null")) {
1957
2.31k
    absl::StatusOr<int> offset = Jump::CalculateOffset(
1958
2.31k
        *jump_after_first_pos_, visitor_->GetCurrentIndex());
1959
2.31k
    if (!offset.ok()) {
1960
0
      visitor_->SetProgressStatusIfError(offset.status());
1961
2.31k
    } else {
1962
2.31k
      fixed_info->set = true;
1963
2.31k
      fixed_info->offset = *offset;
1964
2.31k
    }
1965
2.31k
  }
1966
2.31k
}
1967
1968
0
void ExhaustiveTernaryCondVisitor::PreVisit(const cel::Expr* expr) {
1969
0
  visitor_->ValidateOrError(
1970
0
      !expr->call_expr().has_target() && expr->call_expr().args().size() == 3,
1971
0
      "Invalid argument count for a ternary function call.");
1972
0
}
1973
1974
0
void ExhaustiveTernaryCondVisitor::PostVisit(const cel::Expr* expr) {
1975
0
  visitor_->AddStep(CreateTernaryStep(), expr->id());
1976
0
}
1977
1978
17.1k
void ComprehensionVisitor::PreVisit(const cel::Expr* expr) {
1979
17.1k
  if (is_trivial_) {
1980
0
    visitor_->SuppressBranch(&expr->comprehension_expr().iter_range());
1981
0
    visitor_->SuppressBranch(&expr->comprehension_expr().loop_condition());
1982
0
    visitor_->SuppressBranch(&expr->comprehension_expr().loop_step());
1983
0
  }
1984
17.1k
}
1985
1986
30.2k
ComprehensionCondStep* absl_nullable ComprehensionVisitor::GetCondStep() {
1987
30.2k
  if (!cond_step_pos_) {
1988
0
    return nullptr;
1989
0
  }
1990
30.2k
  ExpressionStep* step =
1991
30.2k
      cond_step_pos_->subexpression->GetIfExpressionStep(cond_step_pos_->index);
1992
30.2k
  if (!step) {
1993
0
    return nullptr;
1994
0
  }
1995
30.2k
  return GetIfComprehensionCondStep(*step);
1996
30.2k
}
1997
1998
30.2k
ComprehensionNextStep* absl_nullable ComprehensionVisitor::GetNextStep() {
1999
30.2k
  if (!next_step_pos_) {
2000
0
    return nullptr;
2001
0
  }
2002
30.2k
  ExpressionStep* step =
2003
30.2k
      next_step_pos_->subexpression->GetIfExpressionStep(next_step_pos_->index);
2004
30.2k
  if (!step) {
2005
0
    return nullptr;
2006
0
  }
2007
30.2k
  return GetIfComprehensionNextStep(*step);
2008
30.2k
}
2009
2010
absl::Status ComprehensionVisitor::PostVisitArgDefault(
2011
75.8k
    cel::ComprehensionArg arg_num, const cel::Expr* expr) {
2012
75.8k
  switch (arg_num) {
2013
15.2k
    case cel::ITER_RANGE: {
2014
15.2k
      init_step_pos_ = visitor_->GetCurrentIndex();
2015
15.2k
      if (iter_slot_ != iter2_slot_) {
2016
0
        init_step_ = visitor_->AddStep(std::make_unique<ComprehensionInitStep>(
2017
0
            iter_slot_, iter2_slot_, accu_slot_));
2018
15.2k
      } else {
2019
15.2k
        init_step_ = visitor_->AddStep(
2020
15.2k
            std::make_unique<ComprehensionInitStep>(iter_slot_, accu_slot_));
2021
15.2k
      }
2022
15.2k
      break;
2023
0
    }
2024
15.2k
    case cel::ACCU_INIT: {
2025
15.2k
      next_step_pos_ = visitor_->GetCurrentIndex();
2026
15.2k
      if (iter_slot_ != iter2_slot_) {
2027
0
        visitor_->AddStep(ExpressionStep::MakeComprehensionNext2Step());
2028
15.2k
      } else {
2029
15.2k
        visitor_->AddStep(ExpressionStep::MakeComprehensionNextStep());
2030
15.2k
      }
2031
15.2k
      break;
2032
0
    }
2033
15.2k
    case cel::LOOP_CONDITION: {
2034
15.2k
      cond_step_pos_ = visitor_->GetCurrentIndex();
2035
15.2k
      if (iter_slot_ != iter2_slot_) {
2036
0
        visitor_->AddStep(ExpressionStep::MakeComprehensionCond2Step());
2037
15.2k
      } else {
2038
15.2k
        visitor_->AddStep(ExpressionStep::MakeComprehensionCondStep());
2039
15.2k
      }
2040
15.2k
      break;
2041
0
    }
2042
15.1k
    case cel::LOOP_STEP: {
2043
15.1k
      ProgramStepIndex index = visitor_->GetCurrentIndex();
2044
15.1k
      ExpressionStep* jump_step =
2045
15.1k
          visitor_->AddStep(ExpressionStep::MakeFixedJumpStep());
2046
15.1k
      FixedJumpStepInfo* fixed_info = GetIfFixedJumpStep(jump_step);
2047
15.1k
      if (fixed_info == nullptr) {
2048
        // either an error occurred earlier or planning is suppressed.
2049
0
        break;
2050
0
      }
2051
30.2k
      CEL_ASSIGN_OR_RETURN(int offset,
2052
30.2k
                           Jump::CalculateOffset(index, *next_step_pos_));
2053
30.2k
      fixed_info->set = true;
2054
30.2k
      fixed_info->offset = offset;
2055
2056
      // Set offsets jumping to the result step.
2057
30.2k
      if (auto* cond_step = GetCondStep(); cond_step != nullptr) {
2058
15.1k
        CEL_ASSIGN_OR_RETURN(int jump_from_cond,
2059
15.1k
                             Jump::CalculateOffset(
2060
15.1k
                                 *cond_step_pos_, visitor_->GetCurrentIndex()));
2061
15.1k
        cond_step->set_jump_offset(jump_from_cond);
2062
15.1k
      } else {
2063
0
        return absl::InvalidArgumentError("Comprehension is malformed");
2064
0
      }
2065
2066
15.1k
      if (auto* next_step = GetNextStep(); next_step != nullptr) {
2067
15.1k
        CEL_ASSIGN_OR_RETURN(int jump_from_next,
2068
15.1k
                             Jump::CalculateOffset(
2069
15.1k
                                 *next_step_pos_, visitor_->GetCurrentIndex()));
2070
2071
15.1k
        next_step->set_jump_offset(jump_from_next);
2072
15.1k
      } else {
2073
0
        return absl::InvalidArgumentError("Comprehension is malformed");
2074
0
      }
2075
15.1k
      break;
2076
15.1k
    }
2077
15.1k
    case cel::RESULT: {
2078
15.1k
      if (!init_step_ || !next_step_pos_ || !cond_step_pos_) {
2079
        // Encountered an error earlier. Can't determine where to jump.
2080
0
        break;
2081
0
      }
2082
15.1k
      visitor_->AddStep(
2083
15.1k
          ExpressionStep::MakeComprehensionFinishStep(accu_slot_, expr->id()));
2084
      // Set offsets jumping past the result step in case of errors.
2085
15.1k
      CEL_ASSIGN_OR_RETURN(
2086
15.1k
          int jump_from_init,
2087
15.1k
          Jump::CalculateOffset(init_step_pos_, visitor_->GetCurrentIndex()));
2088
15.1k
      init_step_->set_error_jump_offset(jump_from_init);
2089
2090
15.1k
      if (auto* next_step = GetNextStep(); next_step != nullptr) {
2091
15.1k
        CEL_ASSIGN_OR_RETURN(int jump_from_next,
2092
15.1k
                             Jump::CalculateOffset(
2093
15.1k
                                 *next_step_pos_, visitor_->GetCurrentIndex()));
2094
15.1k
        next_step->set_error_jump_offset(jump_from_next);
2095
15.1k
      } else {
2096
0
        return absl::InvalidArgumentError("Comprehension is malformed");
2097
0
      }
2098
2099
15.1k
      if (auto* cond_step = GetCondStep(); cond_step != nullptr) {
2100
15.1k
        CEL_ASSIGN_OR_RETURN(int jump_from_cond,
2101
15.1k
                             Jump::CalculateOffset(
2102
15.1k
                                 *cond_step_pos_, visitor_->GetCurrentIndex()));
2103
15.1k
        cond_step->set_error_jump_offset(jump_from_cond);
2104
15.1k
      } else {
2105
0
        return absl::InvalidArgumentError("Comprehension is malformed");
2106
0
      }
2107
15.1k
      break;
2108
15.1k
    }
2109
75.8k
  }
2110
75.8k
  return absl::OkStatus();
2111
75.8k
}
2112
2113
void ComprehensionVisitor::PostVisitArgTrivial(cel::ComprehensionArg arg_num,
2114
0
                                               const cel::Expr* expr) {
2115
0
  switch (arg_num) {
2116
0
    case cel::ITER_RANGE: {
2117
0
      break;
2118
0
    }
2119
0
    case cel::ACCU_INIT: {
2120
0
      if (!accu_init_extracted_) {
2121
0
        visitor_->AddStep(ExpressionStep::MakeAssignSlotAndPopStep(accu_slot_));
2122
0
      }
2123
0
      break;
2124
0
    }
2125
0
    case cel::LOOP_CONDITION: {
2126
0
      break;
2127
0
    }
2128
0
    case cel::LOOP_STEP: {
2129
0
      break;
2130
0
    }
2131
0
    case cel::RESULT: {
2132
0
      visitor_->AddStep(
2133
0
          ExpressionStep::MakeClearSlotStep(accu_slot_, expr->id()));
2134
0
      break;
2135
0
    }
2136
0
  }
2137
0
}
2138
2139
15.1k
void ComprehensionVisitor::PostVisit(const cel::Expr* expr) {
2140
15.1k
}
2141
2142
// Flattens the expression table into the end of the mainline expression vector
2143
// and returns an index to the individual sub expressions.
2144
std::vector<ExecutionPathView> FlattenExpressionTable(
2145
17.3k
    ProgramBuilder& program_builder, ExecutionPath& main) {
2146
17.3k
  std::vector<std::pair<size_t, size_t>> ranges;
2147
17.3k
  main = program_builder.FlattenMain();
2148
17.3k
  ranges.push_back(std::make_pair(0, main.size()));
2149
2150
17.3k
  std::vector<ExecutionPath> subexpressions =
2151
17.3k
      program_builder.FlattenSubexpressions();
2152
17.3k
  for (auto& subexpression : subexpressions) {
2153
0
    ranges.push_back(std::make_pair(main.size(), subexpression.size()));
2154
0
    absl::c_move(subexpression, std::back_inserter(main));
2155
0
  }
2156
2157
17.3k
  std::vector<ExecutionPathView> subexpression_indexes;
2158
17.3k
  subexpression_indexes.reserve(ranges.size());
2159
17.3k
  for (const auto& range : ranges) {
2160
17.3k
    subexpression_indexes.push_back(
2161
17.3k
        absl::MakeSpan(main).subspan(range.first, range.second));
2162
17.3k
  }
2163
17.3k
  return subexpression_indexes;
2164
17.3k
}
2165
2166
absl::Status CheckAstExtensions(
2167
17.9k
    const std::vector<cel::ExtensionSpec>& extensions) {
2168
17.9k
  for (const cel::ExtensionSpec& extension : extensions) {
2169
0
    if (extension.id() == "cel_block" && extension.version().major() == 1) {
2170
      // cel_block v1 is always supported.
2171
0
      continue;
2172
0
    }
2173
2174
    // TODO(uncreated-issue/89): Add support for json field names.
2175
0
    return absl::InvalidArgumentError(absl::StrCat(
2176
0
        "unsupported CEL extension: ", extension.id(), "@",
2177
0
        extension.version().major(), ".", extension.version().minor()));
2178
0
  }
2179
17.9k
  return absl::OkStatus();
2180
17.9k
}
2181
2182
}  // namespace
2183
2184
absl::StatusOr<FlatExpression> FlatExprBuilder::CreateExpressionImpl(
2185
17.9k
    std::unique_ptr<Ast> ast, std::vector<RuntimeIssue>* issues) const {
2186
17.9k
  if (absl::StartsWith(container_, ".") || absl::EndsWith(container_, ".")) {
2187
0
    return absl::InvalidArgumentError(
2188
0
        absl::StrCat("Invalid expression container: '", container_, "'"));
2189
0
  }
2190
2191
17.9k
  RuntimeIssue::Severity max_severity = options_.fail_on_warnings
2192
17.9k
                                            ? RuntimeIssue::Severity::kWarning
2193
17.9k
                                            : RuntimeIssue::Severity::kError;
2194
17.9k
  IssueCollector issue_collector(max_severity);
2195
2196
17.9k
  absl::StatusOr<std::vector<cel::ExtensionSpec>> runtime_extensions =
2197
17.9k
      ExtractAndValidateRuntimeExtensions(*ast);
2198
2199
17.9k
  if (!runtime_extensions.ok()) {
2200
0
    CEL_RETURN_IF_ERROR(issue_collector.AddIssue(
2201
0
        RuntimeIssue::CreateError(runtime_extensions.status())));
2202
0
  }
2203
2204
17.9k
  auto status = CheckAstExtensions(*runtime_extensions);
2205
17.9k
  if (!status.ok()) {
2206
0
    CEL_RETURN_IF_ERROR(
2207
0
        issue_collector.AddIssue(RuntimeIssue::CreateError(status)));
2208
0
  }
2209
2210
17.9k
  Resolver resolver(container_, function_registry_, type_registry_,
2211
17.9k
                    GetTypeProvider(),
2212
17.9k
                    options_.enable_qualified_type_identifiers);
2213
2214
17.9k
  std::shared_ptr<google::protobuf::Arena> arena;
2215
17.9k
  ProgramBuilder program_builder;
2216
17.9k
  PlannerContext extension_context(env_, resolver, options_, GetTypeProvider(),
2217
17.9k
                                   issue_collector, program_builder, arena);
2218
2219
17.9k
  for (const std::unique_ptr<AstTransform>& transform : ast_transforms_) {
2220
17.9k
    CEL_RETURN_IF_ERROR(transform->UpdateAst(extension_context, *ast));
2221
17.9k
  }
2222
2223
17.9k
  std::vector<std::unique_ptr<ProgramOptimizer>> optimizers;
2224
17.9k
  for (const ProgramOptimizerFactory& optimizer_factory : program_optimizers_) {
2225
0
    CEL_ASSIGN_OR_RETURN(auto optimizer,
2226
0
                         optimizer_factory(extension_context, *ast));
2227
0
    if (optimizer != nullptr) {
2228
0
      optimizers.push_back(std::move(optimizer));
2229
0
    }
2230
0
  }
2231
2232
  // These objects are expected to remain scoped to one build call -- references
2233
  // to them shouldn't be persisted in any part of the result expression.
2234
17.9k
  FlatExprVisitor visitor(resolver, options_, std::move(optimizers),
2235
17.9k
                          ast->type_map(), GetTypeProvider(), issue_collector,
2236
17.9k
                          program_builder, extension_context,
2237
17.9k
                          enable_optional_types_);
2238
2239
17.9k
  cel::TraversalOptions opts;
2240
17.9k
  opts.use_comprehension_callbacks = true;
2241
17.9k
  AstTraverse(ast->root_expr(), visitor, opts);
2242
2243
17.9k
  if (!visitor.progress_status().ok()) {
2244
587
    return visitor.progress_status();
2245
587
  }
2246
2247
17.3k
  if (visitor.slot_count() > std::numeric_limits<uint32_t>::max() ||
2248
17.3k
      program_builder.ExtractedSubexpressionCount() >
2249
17.3k
          std::numeric_limits<uint32_t>::max()) {
2250
    // Impractical to trigger (we'd run out of memory first), but assuming this
2251
    // allows us to pack references to slots and subexpressions.
2252
0
    return absl::InternalError(
2253
0
        "Expression too large to be executed, exceeds uint32_t limits.");
2254
0
  }
2255
2256
17.3k
  if (issues != nullptr) {
2257
0
    (*issues) = issue_collector.ExtractIssues();
2258
0
  }
2259
2260
17.3k
  ExecutionPath execution_path;
2261
17.3k
  std::vector<ExecutionPathView> subexpressions =
2262
17.3k
      FlattenExpressionTable(program_builder, execution_path);
2263
2264
17.3k
  return FlatExpression(std::move(execution_path), std::move(subexpressions),
2265
17.3k
                        visitor.slot_count(), GetTypeProvider(), options_,
2266
17.3k
                        std::move(arena));
2267
17.3k
}
2268
71.0k
const cel::TypeProvider& FlatExprBuilder::GetTypeProvider() const {
2269
71.0k
  return use_legacy_type_provider_
2270
71.0k
             ? static_cast<const cel::TypeProvider&>(
2271
71.0k
                   GetLegacyRuntimeTypeProvider(type_registry_))
2272
71.0k
             : GetRuntimeTypeProvider(type_registry_);
2273
71.0k
}
2274
2275
}  // namespace google::api::expr::runtime