Coverage Report

Created: 2026-09-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/common/ast_traverse.cc
Line
Count
Source
1
// Copyright 2018 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_traverse.h"
16
17
#include <memory>
18
#include <stack>
19
20
#include "absl/log/absl_log.h"
21
#include "absl/types/variant.h"
22
#include "common/ast_visitor.h"
23
#include "common/constant.h"
24
#include "common/expr.h"
25
26
namespace cel {
27
28
namespace {
29
30
struct ArgRecord {
31
  // Not null.
32
  const Expr* expr;
33
34
  // For records that are direct arguments to call, we need to call
35
  // the CallArg visitor immediately after the argument is evaluated.
36
  const Expr* calling_expr;
37
  int call_arg;
38
};
39
40
struct ComprehensionRecord {
41
  // Not null.
42
  const Expr* expr;
43
44
  const ComprehensionExpr* comprehension;
45
  const Expr* comprehension_expr;
46
  ComprehensionArg comprehension_arg;
47
  bool use_comprehension_callbacks;
48
};
49
50
struct ExprRecord {
51
  // Not null.
52
  const Expr* expr;
53
};
54
55
using StackRecordKind =
56
    std::variant<ExprRecord, ArgRecord, ComprehensionRecord>;
57
58
struct StackRecord {
59
 public:
60
  static constexpr int kTarget = -2;
61
62
487k
  explicit StackRecord(const Expr* e) {
63
487k
    ExprRecord record;
64
487k
    record.expr = e;
65
487k
    record_variant = record;
66
487k
  }
67
68
  StackRecord(const Expr* e, const ComprehensionExpr* comprehension,
69
              const Expr* comprehension_expr,
70
              ComprehensionArg comprehension_arg,
71
75.5k
              bool use_comprehension_callbacks) {
72
75.5k
    if (use_comprehension_callbacks) {
73
75.5k
      ComprehensionRecord record;
74
75.5k
      record.expr = e;
75
75.5k
      record.comprehension = comprehension;
76
75.5k
      record.comprehension_expr = comprehension_expr;
77
75.5k
      record.comprehension_arg = comprehension_arg;
78
75.5k
      record.use_comprehension_callbacks = use_comprehension_callbacks;
79
75.5k
      record_variant = record;
80
75.5k
      return;
81
75.5k
    }
82
0
    ArgRecord record;
83
0
    record.expr = e;
84
0
    record.calling_expr = comprehension_expr;
85
0
    record.call_arg = comprehension_arg;
86
0
    record_variant = record;
87
0
  }
88
89
242k
  StackRecord(const Expr* e, const Expr* call, int argnum) {
90
242k
    ArgRecord record;
91
242k
    record.expr = e;
92
242k
    record.calling_expr = call;
93
242k
    record.call_arg = argnum;
94
242k
    record_variant = record;
95
242k
  }
96
  StackRecordKind record_variant;
97
  bool visited = false;
98
};
99
100
struct PreVisitor {
101
487k
  void operator()(const ExprRecord& record) {
102
487k
    const Expr* expr = record.expr;
103
487k
    visitor->PreVisitExpr(*expr);
104
487k
    if (expr->has_select_expr()) {
105
18.0k
      visitor->PreVisitSelect(*expr, expr->select_expr());
106
469k
    } else if (expr->has_call_expr()) {
107
128k
      visitor->PreVisitCall(*expr, expr->call_expr());
108
340k
    } else if (expr->has_comprehension_expr()) {
109
15.1k
      visitor->PreVisitComprehension(*expr, expr->comprehension_expr());
110
325k
    } else {
111
      // No pre-visit action.
112
325k
    }
113
487k
  }
114
115
  // Do nothing for Arg variant.
116
242k
  void operator()(const ArgRecord&) {}
117
118
75.5k
  void operator()(const ComprehensionRecord& record) {
119
75.5k
    visitor->PreVisitComprehensionSubexpression(*record.comprehension_expr,
120
75.5k
                                                *record.comprehension,
121
75.5k
                                                record.comprehension_arg);
122
75.5k
  }
123
124
  AstVisitor* visitor;
125
};
126
127
806k
void PreVisit(const StackRecord& record, AstVisitor* visitor) {
128
806k
  absl::visit(PreVisitor{visitor}, record.record_variant);
129
806k
}
130
131
struct PostVisitor {
132
487k
  void operator()(const ExprRecord& record) {
133
487k
    const Expr* expr = record.expr;
134
487k
    struct {
135
487k
      AstVisitor* visitor;
136
487k
      const Expr* expr;
137
487k
      void operator()(const Constant& constant) {
138
173k
        visitor->PostVisitConst(*expr, expr->const_expr());
139
173k
      }
140
487k
      void operator()(const IdentExpr& ident) {
141
93.1k
        visitor->PostVisitIdent(*expr, expr->ident_expr());
142
93.1k
      }
143
487k
      void operator()(const SelectExpr& select) {
144
18.0k
        visitor->PostVisitSelect(*expr, expr->select_expr());
145
18.0k
      }
146
487k
      void operator()(const CallExpr& call) {
147
128k
        visitor->PostVisitCall(*expr, expr->call_expr());
148
128k
      }
149
487k
      void operator()(const ListExpr& create_list) {
150
51.5k
        visitor->PostVisitList(*expr, expr->list_expr());
151
51.5k
      }
152
487k
      void operator()(const StructExpr& create_struct) {
153
2.82k
        visitor->PostVisitStruct(*expr, expr->struct_expr());
154
2.82k
      }
155
487k
      void operator()(const MapExpr& map_expr) {
156
4.82k
        visitor->PostVisitMap(*expr, expr->map_expr());
157
4.82k
      }
158
487k
      void operator()(const ComprehensionExpr& comprehension) {
159
15.1k
        visitor->PostVisitComprehension(*expr, expr->comprehension_expr());
160
15.1k
      }
161
487k
      void operator()(const UnspecifiedExpr&) {
162
0
        ABSL_LOG(ERROR) << "Unsupported Expr kind";
163
0
      }
164
487k
    } handler{visitor, record.expr};
165
487k
    absl::visit(handler, record.expr->kind());
166
167
487k
    visitor->PostVisitExpr(*expr);
168
487k
  }
169
170
242k
  void operator()(const ArgRecord& record) {
171
242k
    if (record.call_arg == StackRecord::kTarget) {
172
1.10k
      visitor->PostVisitTarget(*record.calling_expr);
173
241k
    } else {
174
241k
      visitor->PostVisitArg(*record.calling_expr, record.call_arg);
175
241k
    }
176
242k
  }
177
178
75.5k
  void operator()(const ComprehensionRecord& record) {
179
75.5k
    visitor->PostVisitComprehensionSubexpression(*record.comprehension_expr,
180
75.5k
                                                 *record.comprehension,
181
75.5k
                                                 record.comprehension_arg);
182
75.5k
  }
183
184
  AstVisitor* visitor;
185
};
186
187
806k
void PostVisit(const StackRecord& record, AstVisitor* visitor) {
188
806k
  absl::visit(PostVisitor{visitor}, record.record_variant);
189
806k
}
190
191
void PushSelectDeps(const SelectExpr* select_expr,
192
18.0k
                    std::stack<StackRecord>* stack) {
193
18.0k
  if (select_expr->has_operand()) {
194
18.0k
    stack->push(StackRecord(&select_expr->operand()));
195
18.0k
  }
196
18.0k
}
197
198
void PushCallDeps(const CallExpr* call_expr, const Expr* expr,
199
128k
                  std::stack<StackRecord>* stack) {
200
128k
  const int arg_size = call_expr->args().size();
201
  // Our contract is that we visit arguments in order.  To do that, we need
202
  // to push them onto the stack in reverse order.
203
370k
  for (int i = arg_size - 1; i >= 0; --i) {
204
241k
    stack->push(StackRecord(&call_expr->args()[i], expr, i));
205
241k
  }
206
  // Are we receiver-style?
207
128k
  if (call_expr->has_target()) {
208
1.10k
    stack->push(StackRecord(&call_expr->target(), expr, StackRecord::kTarget));
209
1.10k
  }
210
128k
}
211
212
51.5k
void PushListDeps(const ListExpr* list_expr, std::stack<StackRecord>* stack) {
213
51.5k
  const auto& elements = list_expr->elements();
214
167k
  for (auto it = elements.rbegin(); it != elements.rend(); ++it) {
215
115k
    const auto& element = *it;
216
115k
    stack->push(StackRecord(&element.expr()));
217
115k
  }
218
51.5k
}
219
220
void PushStructDeps(const StructExpr* struct_expr,
221
2.82k
                    std::stack<StackRecord>* stack) {
222
2.82k
  const auto& entries = struct_expr->fields();
223
5.62k
  for (auto it = entries.rbegin(); it != entries.rend(); ++it) {
224
2.79k
    const auto& entry = *it;
225
    // The contract is to visit key, then value.  So put them on the stack
226
    // in the opposite order.
227
2.79k
    if (entry.has_value()) {
228
2.79k
      stack->push(StackRecord(&entry.value()));
229
2.79k
    }
230
2.79k
  }
231
2.82k
}
232
233
4.82k
void PushMapDeps(const MapExpr* map_expr, std::stack<StackRecord>* stack) {
234
4.82k
  const auto& entries = map_expr->entries();
235
11.1k
  for (auto it = entries.rbegin(); it != entries.rend(); ++it) {
236
6.35k
    const auto& entry = *it;
237
    // The contract is to visit key, then value.  So put them on the stack
238
    // in the opposite order.
239
6.35k
    if (entry.has_value()) {
240
6.35k
      stack->push(StackRecord(&entry.value()));
241
6.35k
    }
242
    // The contract is to visit key, then value.  So put them on the stack
243
    // in the opposite order.
244
6.35k
    if (entry.has_key()) {
245
6.35k
      stack->push(StackRecord(&entry.key()));
246
6.35k
    }
247
6.35k
  }
248
4.82k
}
249
250
void PushComprehensionDeps(const ComprehensionExpr* c, const Expr* expr,
251
                           std::stack<StackRecord>* stack,
252
15.1k
                           bool use_comprehension_callbacks) {
253
15.1k
  StackRecord iter_range(&c->iter_range(), c, expr, ITER_RANGE,
254
15.1k
                         use_comprehension_callbacks);
255
15.1k
  StackRecord accu_init(&c->accu_init(), c, expr, ACCU_INIT,
256
15.1k
                        use_comprehension_callbacks);
257
15.1k
  StackRecord loop_condition(&c->loop_condition(), c, expr, LOOP_CONDITION,
258
15.1k
                             use_comprehension_callbacks);
259
15.1k
  StackRecord loop_step(&c->loop_step(), c, expr, LOOP_STEP,
260
15.1k
                        use_comprehension_callbacks);
261
15.1k
  StackRecord result(&c->result(), c, expr, RESULT,
262
15.1k
                     use_comprehension_callbacks);
263
  // Push them in reverse order.
264
15.1k
  stack->push(result);
265
15.1k
  stack->push(loop_step);
266
15.1k
  stack->push(loop_condition);
267
15.1k
  stack->push(accu_init);
268
15.1k
  stack->push(iter_range);
269
15.1k
}
270
271
struct PushDepsVisitor {
272
487k
  void operator()(const ExprRecord& record) {
273
487k
    struct {
274
487k
      std::stack<StackRecord>& stack;
275
487k
      const TraversalOptions& options;
276
487k
      const ExprRecord& record;
277
487k
      void operator()(const Constant& constant) {}
278
487k
      void operator()(const IdentExpr& ident) {}
279
487k
      void operator()(const SelectExpr& select) {
280
18.0k
        PushSelectDeps(&record.expr->select_expr(), &stack);
281
18.0k
      }
282
487k
      void operator()(const CallExpr& call) {
283
128k
        PushCallDeps(&record.expr->call_expr(), record.expr, &stack);
284
128k
      }
285
487k
      void operator()(const ListExpr& create_list) {
286
51.5k
        PushListDeps(&record.expr->list_expr(), &stack);
287
51.5k
      }
288
487k
      void operator()(const StructExpr& create_struct) {
289
2.82k
        PushStructDeps(&record.expr->struct_expr(), &stack);
290
2.82k
      }
291
487k
      void operator()(const MapExpr& map_expr) {
292
4.82k
        PushMapDeps(&record.expr->map_expr(), &stack);
293
4.82k
      }
294
487k
      void operator()(const ComprehensionExpr& comprehension) {
295
15.1k
        PushComprehensionDeps(&record.expr->comprehension_expr(), record.expr,
296
15.1k
                              &stack, options.use_comprehension_callbacks);
297
15.1k
      }
298
487k
      void operator()(const UnspecifiedExpr&) {}
299
487k
    } handler{stack, options, record};
300
487k
    absl::visit(handler, record.expr->kind());
301
487k
  }
302
303
242k
  void operator()(const ArgRecord& record) {
304
242k
    stack.push(StackRecord(record.expr));
305
242k
  }
306
307
75.5k
  void operator()(const ComprehensionRecord& record) {
308
75.5k
    stack.push(StackRecord(record.expr));
309
75.5k
  }
310
311
  std::stack<StackRecord>& stack;
312
  const TraversalOptions& options;
313
};
314
315
void PushDependencies(const StackRecord& record, std::stack<StackRecord>& stack,
316
806k
                      const TraversalOptions& options) {
317
806k
  absl::visit(PushDepsVisitor{stack, options}, record.record_variant);
318
806k
}
319
320
}  // namespace
321
322
namespace common_internal {
323
struct AstTraversalState {
324
  std::stack<StackRecord> stack;
325
};
326
}  // namespace common_internal
327
328
AstTraversal AstTraversal::Create(const cel::Expr& ast,
329
0
                                  const TraversalOptions& options) {
330
0
  AstTraversal instance(options);
331
0
  instance.state_ = std::make_unique<common_internal::AstTraversalState>();
332
0
  instance.state_->stack.push(StackRecord(&ast));
333
0
  return instance;
334
0
}
335
336
0
AstTraversal::AstTraversal(TraversalOptions options) : options_(options) {}
337
338
0
AstTraversal::~AstTraversal() = default;
339
340
0
bool AstTraversal::Step(AstVisitor& visitor) {
341
0
  if (IsDone()) {
342
0
    return false;
343
0
  }
344
0
  auto& stack = state_->stack;
345
0
  StackRecord& record = stack.top();
346
0
  if (!record.visited) {
347
0
    PreVisit(record, &visitor);
348
0
    PushDependencies(record, stack, options_);
349
0
    record.visited = true;
350
0
  } else {
351
0
    PostVisit(record, &visitor);
352
0
    stack.pop();
353
0
  }
354
355
0
  return !stack.empty();
356
0
}
357
358
0
bool AstTraversal::IsDone() {
359
0
  return state_ == nullptr || state_->stack.empty();
360
0
}
361
362
void AstTraverse(const Expr& expr, AstVisitor& visitor,
363
19.5k
                 TraversalOptions options) {
364
19.5k
  std::stack<StackRecord> stack;
365
19.5k
  stack.push(StackRecord(&expr));
366
367
1.63M
  while (!stack.empty()) {
368
1.61M
    StackRecord& record = stack.top();
369
1.61M
    if (!record.visited) {
370
806k
      PreVisit(record, &visitor);
371
806k
      PushDependencies(record, stack, options);
372
806k
      record.visited = true;
373
806k
    } else {
374
806k
      PostVisit(record, &visitor);
375
806k
      stack.pop();
376
806k
    }
377
1.61M
  }
378
19.5k
}
379
380
}  // namespace cel