Coverage Report

Created: 2025-11-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/common/ast/expr_proto.cc
Line
Count
Source
1
// Copyright 2024 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "common/ast/expr_proto.h"
16
17
#include <algorithm>
18
#include <cstddef>
19
#include <cstdint>
20
#include <stack>
21
#include <vector>
22
23
#include "cel/expr/syntax.pb.h"
24
#include "google/protobuf/struct.pb.h"
25
#include "absl/base/attributes.h"
26
#include "absl/base/nullability.h"
27
#include "absl/functional/overload.h"
28
#include "absl/status/status.h"
29
#include "absl/strings/str_cat.h"
30
#include "absl/types/variant.h"
31
#include "common/ast/constant_proto.h"
32
#include "common/constant.h"
33
#include "common/expr.h"
34
#include "internal/status_macros.h"
35
36
namespace cel::ast_internal {
37
38
namespace {
39
40
using ExprProto = cel::expr::Expr;
41
using ConstantProto = cel::expr::Constant;
42
using StructExprProto = cel::expr::Expr::CreateStruct;
43
44
class ExprToProtoState final {
45
 private:
46
  struct Frame final {
47
    const Expr* absl_nonnull expr;
48
    cel::expr::Expr* absl_nonnull proto;
49
  };
50
51
 public:
52
  absl::Status ExprToProto(const Expr& expr,
53
1.32k
                           cel::expr::Expr* absl_nonnull proto) {
54
1.32k
    Push(expr, proto);
55
1.32k
    Frame frame;
56
1.41M
    while (Pop(frame)) {
57
1.41M
      CEL_RETURN_IF_ERROR(ExprToProtoImpl(*frame.expr, frame.proto));
58
1.41M
    }
59
1.32k
    return absl::OkStatus();
60
1.32k
  }
61
62
 private:
63
  absl::Status ExprToProtoImpl(const Expr& expr,
64
1.41M
                               cel::expr::Expr* absl_nonnull proto) {
65
1.41M
    return absl::visit(
66
1.41M
        absl::Overload(
67
1.41M
            [&expr, proto](const UnspecifiedExpr&) -> absl::Status {
68
0
              proto->Clear();
69
0
              proto->set_id(expr.id());
70
0
              return absl::OkStatus();
71
0
            },
72
1.41M
            [this, &expr, proto](const Constant& const_expr) -> absl::Status {
73
612k
              return ConstExprToProto(expr, const_expr, proto);
74
612k
            },
75
1.41M
            [this, &expr, proto](const IdentExpr& ident_expr) -> absl::Status {
76
381k
              return IdentExprToProto(expr, ident_expr, proto);
77
381k
            },
78
1.41M
            [this, &expr,
79
1.41M
             proto](const SelectExpr& select_expr) -> absl::Status {
80
752
              return SelectExprToProto(expr, select_expr, proto);
81
752
            },
82
1.41M
            [this, &expr, proto](const CallExpr& call_expr) -> absl::Status {
83
414k
              return CallExprToProto(expr, call_expr, proto);
84
414k
            },
85
1.41M
            [this, &expr, proto](const ListExpr& list_expr) -> absl::Status {
86
474
              return ListExprToProto(expr, list_expr, proto);
87
474
            },
88
1.41M
            [this, &expr,
89
1.41M
             proto](const StructExpr& struct_expr) -> absl::Status {
90
145
              return StructExprToProto(expr, struct_expr, proto);
91
145
            },
92
1.41M
            [this, &expr, proto](const MapExpr& map_expr) -> absl::Status {
93
189
              return MapExprToProto(expr, map_expr, proto);
94
189
            },
95
1.41M
            [this, &expr, proto](
96
1.41M
                const ComprehensionExpr& comprehension_expr) -> absl::Status {
97
221
              return ComprehensionExprToProto(expr, comprehension_expr, proto);
98
221
            }),
99
1.41M
        expr.kind());
100
1.41M
  }
101
102
  absl::Status ConstExprToProto(const Expr& expr, const Constant& const_expr,
103
612k
                                ExprProto* absl_nonnull proto) {
104
612k
    proto->Clear();
105
612k
    proto->set_id(expr.id());
106
612k
    return ConstantToProto(const_expr, proto->mutable_const_expr());
107
612k
  }
108
109
  absl::Status IdentExprToProto(const Expr& expr, const IdentExpr& ident_expr,
110
381k
                                ExprProto* absl_nonnull proto) {
111
381k
    proto->Clear();
112
381k
    auto* ident_proto = proto->mutable_ident_expr();
113
381k
    proto->set_id(expr.id());
114
381k
    ident_proto->set_name(ident_expr.name());
115
381k
    return absl::OkStatus();
116
381k
  }
117
118
  absl::Status SelectExprToProto(const Expr& expr,
119
                                 const SelectExpr& select_expr,
120
752
                                 ExprProto* absl_nonnull proto) {
121
752
    proto->Clear();
122
752
    auto* select_proto = proto->mutable_select_expr();
123
752
    proto->set_id(expr.id());
124
752
    if (select_expr.has_operand()) {
125
752
      Push(select_expr.operand(), select_proto->mutable_operand());
126
752
    }
127
752
    select_proto->set_field(select_expr.field());
128
752
    select_proto->set_test_only(select_expr.test_only());
129
752
    return absl::OkStatus();
130
752
  }
131
132
  absl::Status CallExprToProto(const Expr& expr, const CallExpr& call_expr,
133
414k
                               ExprProto* absl_nonnull proto) {
134
414k
    proto->Clear();
135
414k
    auto* call_proto = proto->mutable_call_expr();
136
414k
    proto->set_id(expr.id());
137
414k
    if (call_expr.has_target()) {
138
197
      Push(call_expr.target(), call_proto->mutable_target());
139
197
    }
140
414k
    call_proto->set_function(call_expr.function());
141
414k
    if (!call_expr.args().empty()) {
142
414k
      call_proto->mutable_args()->Reserve(
143
414k
          static_cast<int>(call_expr.args().size()));
144
797k
      for (const auto& argument : call_expr.args()) {
145
797k
        Push(argument, call_proto->add_args());
146
797k
      }
147
414k
    }
148
414k
    return absl::OkStatus();
149
414k
  }
150
151
  absl::Status ListExprToProto(const Expr& expr, const ListExpr& list_expr,
152
474
                               ExprProto* absl_nonnull proto) {
153
474
    proto->Clear();
154
474
    auto* list_proto = proto->mutable_list_expr();
155
474
    proto->set_id(expr.id());
156
474
    if (!list_expr.elements().empty()) {
157
417
      list_proto->mutable_elements()->Reserve(
158
417
          static_cast<int>(list_expr.elements().size()));
159
886
      for (size_t i = 0; i < list_expr.elements().size(); ++i) {
160
469
        const auto& element_expr = list_expr.elements()[i];
161
469
        auto* element_proto = list_proto->add_elements();
162
469
        if (element_expr.has_expr()) {
163
469
          Push(element_expr.expr(), element_proto);
164
469
        }
165
469
        if (element_expr.optional()) {
166
0
          list_proto->add_optional_indices(static_cast<int32_t>(i));
167
0
        }
168
469
      }
169
417
    }
170
474
    return absl::OkStatus();
171
474
  }
172
173
  absl::Status StructExprToProto(const Expr& expr,
174
                                 const StructExpr& struct_expr,
175
145
                                 ExprProto* absl_nonnull proto) {
176
145
    proto->Clear();
177
145
    auto* struct_proto = proto->mutable_struct_expr();
178
145
    proto->set_id(expr.id());
179
145
    struct_proto->set_message_name(struct_expr.name());
180
145
    if (!struct_expr.fields().empty()) {
181
17
      struct_proto->mutable_entries()->Reserve(
182
17
          static_cast<int>(struct_expr.fields().size()));
183
181
      for (const auto& field_expr : struct_expr.fields()) {
184
181
        auto* field_proto = struct_proto->add_entries();
185
181
        field_proto->set_id(field_expr.id());
186
181
        field_proto->set_field_key(field_expr.name());
187
181
        if (field_expr.has_value()) {
188
181
          Push(field_expr.value(), field_proto->mutable_value());
189
181
        }
190
181
        if (field_expr.optional()) {
191
0
          field_proto->set_optional_entry(true);
192
0
        }
193
181
      }
194
17
    }
195
145
    return absl::OkStatus();
196
145
  }
197
198
  absl::Status MapExprToProto(const Expr& expr, const MapExpr& map_expr,
199
189
                              ExprProto* absl_nonnull proto) {
200
189
    proto->Clear();
201
189
    auto* map_proto = proto->mutable_struct_expr();
202
189
    proto->set_id(expr.id());
203
189
    if (!map_expr.entries().empty()) {
204
70
      map_proto->mutable_entries()->Reserve(
205
70
          static_cast<int>(map_expr.entries().size()));
206
304k
      for (const auto& entry_expr : map_expr.entries()) {
207
304k
        auto* entry_proto = map_proto->add_entries();
208
304k
        entry_proto->set_id(entry_expr.id());
209
304k
        if (entry_expr.has_key()) {
210
304k
          Push(entry_expr.key(), entry_proto->mutable_map_key());
211
304k
        }
212
304k
        if (entry_expr.has_value()) {
213
304k
          Push(entry_expr.value(), entry_proto->mutable_value());
214
304k
        }
215
304k
        if (entry_expr.optional()) {
216
0
          entry_proto->set_optional_entry(true);
217
0
        }
218
304k
      }
219
70
    }
220
189
    return absl::OkStatus();
221
189
  }
222
223
  absl::Status ComprehensionExprToProto(
224
      const Expr& expr, const ComprehensionExpr& comprehension_expr,
225
221
      ExprProto* absl_nonnull proto) {
226
221
    proto->Clear();
227
221
    auto* comprehension_proto = proto->mutable_comprehension_expr();
228
221
    proto->set_id(expr.id());
229
221
    comprehension_proto->set_iter_var(comprehension_expr.iter_var());
230
221
    comprehension_proto->set_iter_var2(comprehension_expr.iter_var2());
231
221
    if (comprehension_expr.has_iter_range()) {
232
221
      Push(comprehension_expr.iter_range(),
233
221
           comprehension_proto->mutable_iter_range());
234
221
    }
235
221
    comprehension_proto->set_accu_var(comprehension_expr.accu_var());
236
221
    if (comprehension_expr.has_accu_init()) {
237
221
      Push(comprehension_expr.accu_init(),
238
221
           comprehension_proto->mutable_accu_init());
239
221
    }
240
221
    if (comprehension_expr.has_loop_condition()) {
241
221
      Push(comprehension_expr.loop_condition(),
242
221
           comprehension_proto->mutable_loop_condition());
243
221
    }
244
221
    if (comprehension_expr.has_loop_step()) {
245
221
      Push(comprehension_expr.loop_step(),
246
221
           comprehension_proto->mutable_loop_step());
247
221
    }
248
221
    if (comprehension_expr.has_result()) {
249
221
      Push(comprehension_expr.result(), comprehension_proto->mutable_result());
250
221
    }
251
221
    return absl::OkStatus();
252
221
  }
253
254
1.41M
  void Push(const Expr& expr, ExprProto* absl_nonnull proto) {
255
1.41M
    frames_.push(Frame{&expr, proto});
256
1.41M
  }
257
258
1.41M
  bool Pop(Frame& frame) {
259
1.41M
    if (frames_.empty()) {
260
1.32k
      return false;
261
1.32k
    }
262
1.41M
    frame = frames_.top();
263
1.41M
    frames_.pop();
264
1.41M
    return true;
265
1.41M
  }
266
267
  std::stack<Frame, std::vector<Frame>> frames_;
268
};
269
270
class ExprFromProtoState final {
271
 private:
272
  struct Frame final {
273
    const ExprProto* absl_nonnull proto;
274
    Expr* absl_nonnull expr;
275
  };
276
277
 public:
278
0
  absl::Status ExprFromProto(const ExprProto& proto, Expr& expr) {
279
0
    Push(proto, expr);
280
0
    Frame frame;
281
0
    while (Pop(frame)) {
282
0
      CEL_RETURN_IF_ERROR(ExprFromProtoImpl(*frame.proto, *frame.expr));
283
0
    }
284
0
    return absl::OkStatus();
285
0
  }
286
287
 private:
288
0
  absl::Status ExprFromProtoImpl(const ExprProto& proto, Expr& expr) {
289
0
    switch (proto.expr_kind_case()) {
290
0
      case ExprProto::EXPR_KIND_NOT_SET:
291
0
        expr.Clear();
292
0
        expr.set_id(proto.id());
293
0
        return absl::OkStatus();
294
0
      case ExprProto::kConstExpr:
295
0
        return ConstExprFromProto(proto, proto.const_expr(), expr);
296
0
      case ExprProto::kIdentExpr:
297
0
        return IdentExprFromProto(proto, proto.ident_expr(), expr);
298
0
      case ExprProto::kSelectExpr:
299
0
        return SelectExprFromProto(proto, proto.select_expr(), expr);
300
0
      case ExprProto::kCallExpr:
301
0
        return CallExprFromProto(proto, proto.call_expr(), expr);
302
0
      case ExprProto::kListExpr:
303
0
        return ListExprFromProto(proto, proto.list_expr(), expr);
304
0
      case ExprProto::kStructExpr:
305
0
        if (proto.struct_expr().message_name().empty()) {
306
0
          return MapExprFromProto(proto, proto.struct_expr(), expr);
307
0
        }
308
0
        return StructExprFromProto(proto, proto.struct_expr(), expr);
309
0
      case ExprProto::kComprehensionExpr:
310
0
        return ComprehensionExprFromProto(proto, proto.comprehension_expr(),
311
0
                                          expr);
312
0
      default:
313
0
        return absl::InvalidArgumentError(
314
0
            absl::StrCat("unexpected ExprKindCase: ",
315
0
                         static_cast<int>(proto.expr_kind_case())));
316
0
    }
317
0
  }
318
319
  absl::Status ConstExprFromProto(const ExprProto& proto,
320
                                  const ConstantProto& const_proto,
321
0
                                  Expr& expr) {
322
0
    expr.Clear();
323
0
    expr.set_id(proto.id());
324
0
    return ConstantFromProto(const_proto, expr.mutable_const_expr());
325
0
  }
326
327
  absl::Status IdentExprFromProto(const ExprProto& proto,
328
                                  const ExprProto::Ident& ident_proto,
329
0
                                  Expr& expr) {
330
0
    expr.Clear();
331
0
    expr.set_id(proto.id());
332
0
    auto& ident_expr = expr.mutable_ident_expr();
333
0
    ident_expr.set_name(ident_proto.name());
334
0
    return absl::OkStatus();
335
0
  }
336
337
  absl::Status SelectExprFromProto(const ExprProto& proto,
338
                                   const ExprProto::Select& select_proto,
339
0
                                   Expr& expr) {
340
0
    expr.Clear();
341
0
    expr.set_id(proto.id());
342
0
    auto& select_expr = expr.mutable_select_expr();
343
0
    if (select_proto.has_operand()) {
344
0
      Push(select_proto.operand(), select_expr.mutable_operand());
345
0
    }
346
0
    select_expr.set_field(select_proto.field());
347
0
    select_expr.set_test_only(select_proto.test_only());
348
0
    return absl::OkStatus();
349
0
  }
350
351
  absl::Status CallExprFromProto(const ExprProto& proto,
352
                                 const ExprProto::Call& call_proto,
353
0
                                 Expr& expr) {
354
0
    expr.Clear();
355
0
    expr.set_id(proto.id());
356
0
    auto& call_expr = expr.mutable_call_expr();
357
0
    call_expr.set_function(call_proto.function());
358
0
    if (call_proto.has_target()) {
359
0
      Push(call_proto.target(), call_expr.mutable_target());
360
0
    }
361
0
    call_expr.mutable_args().reserve(
362
0
        static_cast<size_t>(call_proto.args().size()));
363
0
    for (const auto& argument_proto : call_proto.args()) {
364
0
      Push(argument_proto, call_expr.add_args());
365
0
    }
366
0
    return absl::OkStatus();
367
0
  }
368
369
  absl::Status ListExprFromProto(const ExprProto& proto,
370
                                 const ExprProto::CreateList& list_proto,
371
0
                                 Expr& expr) {
372
0
    expr.Clear();
373
0
    expr.set_id(proto.id());
374
0
    auto& list_expr = expr.mutable_list_expr();
375
0
    list_expr.mutable_elements().reserve(
376
0
        static_cast<size_t>(list_proto.elements().size()));
377
0
    for (int i = 0; i < list_proto.elements().size(); ++i) {
378
0
      const auto& element_proto = list_proto.elements()[i];
379
0
      auto& element_expr = list_expr.add_elements();
380
0
      Push(element_proto, element_expr.mutable_expr());
381
0
      const auto& optional_indicies_proto = list_proto.optional_indices();
382
0
      element_expr.set_optional(std::find(optional_indicies_proto.begin(),
383
0
                                          optional_indicies_proto.end(),
384
0
                                          i) != optional_indicies_proto.end());
385
0
    }
386
0
    return absl::OkStatus();
387
0
  }
388
389
  absl::Status StructExprFromProto(const ExprProto& proto,
390
                                   const StructExprProto& struct_proto,
391
0
                                   Expr& expr) {
392
0
    expr.Clear();
393
0
    expr.set_id(proto.id());
394
0
    auto& struct_expr = expr.mutable_struct_expr();
395
0
    struct_expr.set_name(struct_proto.message_name());
396
0
    struct_expr.mutable_fields().reserve(
397
0
        static_cast<size_t>(struct_proto.entries().size()));
398
0
    for (const auto& field_proto : struct_proto.entries()) {
399
0
      switch (field_proto.key_kind_case()) {
400
0
        case StructExprProto::Entry::KEY_KIND_NOT_SET:
401
0
          ABSL_FALLTHROUGH_INTENDED;
402
0
        case StructExprProto::Entry::kFieldKey:
403
0
          break;
404
0
        case StructExprProto::Entry::kMapKey:
405
0
          return absl::InvalidArgumentError("encountered map entry in struct");
406
0
        default:
407
0
          return absl::InvalidArgumentError(absl::StrCat(
408
0
              "unexpected struct field kind: ", field_proto.key_kind_case()));
409
0
      }
410
0
      auto& field_expr = struct_expr.add_fields();
411
0
      field_expr.set_id(field_proto.id());
412
0
      field_expr.set_name(field_proto.field_key());
413
0
      if (field_proto.has_value()) {
414
0
        Push(field_proto.value(), field_expr.mutable_value());
415
0
      }
416
0
      field_expr.set_optional(field_proto.optional_entry());
417
0
    }
418
0
    return absl::OkStatus();
419
0
  }
420
421
  absl::Status MapExprFromProto(const ExprProto& proto,
422
                                const ExprProto::CreateStruct& map_proto,
423
0
                                Expr& expr) {
424
0
    expr.Clear();
425
0
    expr.set_id(proto.id());
426
0
    auto& map_expr = expr.mutable_map_expr();
427
0
    map_expr.mutable_entries().reserve(
428
0
        static_cast<size_t>(map_proto.entries().size()));
429
0
    for (const auto& entry_proto : map_proto.entries()) {
430
0
      switch (entry_proto.key_kind_case()) {
431
0
        case StructExprProto::Entry::KEY_KIND_NOT_SET:
432
0
          ABSL_FALLTHROUGH_INTENDED;
433
0
        case StructExprProto::Entry::kMapKey:
434
0
          break;
435
0
        case StructExprProto::Entry::kFieldKey:
436
0
          return absl::InvalidArgumentError("encountered struct field in map");
437
0
        default:
438
0
          return absl::InvalidArgumentError(absl::StrCat(
439
0
              "unexpected map entry kind: ", entry_proto.key_kind_case()));
440
0
      }
441
0
      auto& entry_expr = map_expr.add_entries();
442
0
      entry_expr.set_id(entry_proto.id());
443
0
      if (entry_proto.has_map_key()) {
444
0
        Push(entry_proto.map_key(), entry_expr.mutable_key());
445
0
      }
446
0
      if (entry_proto.has_value()) {
447
0
        Push(entry_proto.value(), entry_expr.mutable_value());
448
0
      }
449
0
      entry_expr.set_optional(entry_proto.optional_entry());
450
0
    }
451
0
    return absl::OkStatus();
452
0
  }
453
454
  absl::Status ComprehensionExprFromProto(
455
      const ExprProto& proto,
456
0
      const ExprProto::Comprehension& comprehension_proto, Expr& expr) {
457
0
    expr.Clear();
458
0
    expr.set_id(proto.id());
459
0
    auto& comprehension_expr = expr.mutable_comprehension_expr();
460
0
    comprehension_expr.set_iter_var(comprehension_proto.iter_var());
461
0
    comprehension_expr.set_iter_var2(comprehension_proto.iter_var2());
462
0
    comprehension_expr.set_accu_var(comprehension_proto.accu_var());
463
0
    if (comprehension_proto.has_iter_range()) {
464
0
      Push(comprehension_proto.iter_range(),
465
0
           comprehension_expr.mutable_iter_range());
466
0
    }
467
0
    if (comprehension_proto.has_accu_init()) {
468
0
      Push(comprehension_proto.accu_init(),
469
0
           comprehension_expr.mutable_accu_init());
470
0
    }
471
0
    if (comprehension_proto.has_loop_condition()) {
472
0
      Push(comprehension_proto.loop_condition(),
473
0
           comprehension_expr.mutable_loop_condition());
474
0
    }
475
0
    if (comprehension_proto.has_loop_step()) {
476
0
      Push(comprehension_proto.loop_step(),
477
0
           comprehension_expr.mutable_loop_step());
478
0
    }
479
0
    if (comprehension_proto.has_result()) {
480
0
      Push(comprehension_proto.result(), comprehension_expr.mutable_result());
481
0
    }
482
0
    return absl::OkStatus();
483
0
  }
484
485
0
  void Push(const ExprProto& proto, Expr& expr) {
486
0
    frames_.push(Frame{&proto, &expr});
487
0
  }
488
489
0
  bool Pop(Frame& frame) {
490
0
    if (frames_.empty()) {
491
0
      return false;
492
0
    }
493
0
    frame = frames_.top();
494
0
    frames_.pop();
495
0
    return true;
496
0
  }
497
498
  std::stack<Frame, std::vector<Frame>> frames_;
499
};
500
501
}  // namespace
502
503
absl::Status ExprToProto(const Expr& expr,
504
1.32k
                         cel::expr::Expr* absl_nonnull proto) {
505
1.32k
  ExprToProtoState state;
506
1.32k
  return state.ExprToProto(expr, proto);
507
1.32k
}
508
509
0
absl::Status ExprFromProto(const cel::expr::Expr& proto, Expr& expr) {
510
0
  ExprFromProtoState state;
511
0
  return state.ExprFromProto(proto, expr);
512
0
}
513
514
}  // namespace cel::ast_internal