Coverage Report

Created: 2026-09-03 06:30

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
470k
  explicit StackRecord(const Expr* e) {
63
470k
    ExprRecord record;
64
470k
    record.expr = e;
65
470k
    record_variant = record;
66
470k
  }
67
68
  StackRecord(const Expr* e, const ComprehensionExpr* comprehension,
69
              const Expr* comprehension_expr,
70
              ComprehensionArg comprehension_arg,
71
67.3k
              bool use_comprehension_callbacks) {
72
67.3k
    if (use_comprehension_callbacks) {
73
67.3k
      ComprehensionRecord record;
74
67.3k
      record.expr = e;
75
67.3k
      record.comprehension = comprehension;
76
67.3k
      record.comprehension_expr = comprehension_expr;
77
67.3k
      record.comprehension_arg = comprehension_arg;
78
67.3k
      record.use_comprehension_callbacks = use_comprehension_callbacks;
79
67.3k
      record_variant = record;
80
67.3k
      return;
81
67.3k
    }
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
244k
  StackRecord(const Expr* e, const Expr* call, int argnum) {
90
244k
    ArgRecord record;
91
244k
    record.expr = e;
92
244k
    record.calling_expr = call;
93
244k
    record.call_arg = argnum;
94
244k
    record_variant = record;
95
244k
  }
96
  StackRecordKind record_variant;
97
  bool visited = false;
98
};
99
100
struct PreVisitor {
101
470k
  void operator()(const ExprRecord& record) {
102
470k
    const Expr* expr = record.expr;
103
470k
    visitor->PreVisitExpr(*expr);
104
470k
    if (expr->has_select_expr()) {
105
18.6k
      visitor->PreVisitSelect(*expr, expr->select_expr());
106
451k
    } else if (expr->has_call_expr()) {
107
129k
      visitor->PreVisitCall(*expr, expr->call_expr());
108
322k
    } else if (expr->has_comprehension_expr()) {
109
13.4k
      visitor->PreVisitComprehension(*expr, expr->comprehension_expr());
110
309k
    } else {
111
      // No pre-visit action.
112
309k
    }
113
470k
  }
114
115
  // Do nothing for Arg variant.
116
244k
  void operator()(const ArgRecord&) {}
117
118
67.3k
  void operator()(const ComprehensionRecord& record) {
119
67.3k
    visitor->PreVisitComprehensionSubexpression(*record.comprehension_expr,
120
67.3k
                                                *record.comprehension,
121
67.3k
                                                record.comprehension_arg);
122
67.3k
  }
123
124
  AstVisitor* visitor;
125
};
126
127
782k
void PreVisit(const StackRecord& record, AstVisitor* visitor) {
128
782k
  absl::visit(PreVisitor{visitor}, record.record_variant);
129
782k
}
130
131
struct PostVisitor {
132
470k
  void operator()(const ExprRecord& record) {
133
470k
    const Expr* expr = record.expr;
134
470k
    struct {
135
470k
      AstVisitor* visitor;
136
470k
      const Expr* expr;
137
470k
      void operator()(const Constant& constant) {
138
164k
        visitor->PostVisitConst(*expr, expr->const_expr());
139
164k
      }
140
470k
      void operator()(const IdentExpr& ident) {
141
91.1k
        visitor->PostVisitIdent(*expr, expr->ident_expr());
142
91.1k
      }
143
470k
      void operator()(const SelectExpr& select) {
144
18.6k
        visitor->PostVisitSelect(*expr, expr->select_expr());
145
18.6k
      }
146
470k
      void operator()(const CallExpr& call) {
147
129k
        visitor->PostVisitCall(*expr, expr->call_expr());
148
129k
      }
149
470k
      void operator()(const ListExpr& create_list) {
150
46.1k
        visitor->PostVisitList(*expr, expr->list_expr());
151
46.1k
      }
152
470k
      void operator()(const StructExpr& create_struct) {
153
2.31k
        visitor->PostVisitStruct(*expr, expr->struct_expr());
154
2.31k
      }
155
470k
      void operator()(const MapExpr& map_expr) {
156
4.86k
        visitor->PostVisitMap(*expr, expr->map_expr());
157
4.86k
      }
158
470k
      void operator()(const ComprehensionExpr& comprehension) {
159
13.4k
        visitor->PostVisitComprehension(*expr, expr->comprehension_expr());
160
13.4k
      }
161
470k
      void operator()(const UnspecifiedExpr&) {
162
0
        ABSL_LOG(ERROR) << "Unsupported Expr kind";
163
0
      }
164
470k
    } handler{visitor, record.expr};
165
470k
    absl::visit(handler, record.expr->kind());
166
167
470k
    visitor->PostVisitExpr(*expr);
168
470k
  }
169
170
244k
  void operator()(const ArgRecord& record) {
171
244k
    if (record.call_arg == StackRecord::kTarget) {
172
1.22k
      visitor->PostVisitTarget(*record.calling_expr);
173
243k
    } else {
174
243k
      visitor->PostVisitArg(*record.calling_expr, record.call_arg);
175
243k
    }
176
244k
  }
177
178
67.3k
  void operator()(const ComprehensionRecord& record) {
179
67.3k
    visitor->PostVisitComprehensionSubexpression(*record.comprehension_expr,
180
67.3k
                                                 *record.comprehension,
181
67.3k
                                                 record.comprehension_arg);
182
67.3k
  }
183
184
  AstVisitor* visitor;
185
};
186
187
782k
void PostVisit(const StackRecord& record, AstVisitor* visitor) {
188
782k
  absl::visit(PostVisitor{visitor}, record.record_variant);
189
782k
}
190
191
void PushSelectDeps(const SelectExpr* select_expr,
192
18.6k
                    std::stack<StackRecord>* stack) {
193
18.6k
  if (select_expr->has_operand()) {
194
18.6k
    stack->push(StackRecord(&select_expr->operand()));
195
18.6k
  }
196
18.6k
}
197
198
void PushCallDeps(const CallExpr* call_expr, const Expr* expr,
199
129k
                  std::stack<StackRecord>* stack) {
200
129k
  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
372k
  for (int i = arg_size - 1; i >= 0; --i) {
204
243k
    stack->push(StackRecord(&call_expr->args()[i], expr, i));
205
243k
  }
206
  // Are we receiver-style?
207
129k
  if (call_expr->has_target()) {
208
1.22k
    stack->push(StackRecord(&call_expr->target(), expr, StackRecord::kTarget));
209
1.22k
  }
210
129k
}
211
212
46.1k
void PushListDeps(const ListExpr* list_expr, std::stack<StackRecord>* stack) {
213
46.1k
  const auto& elements = list_expr->elements();
214
155k
  for (auto it = elements.rbegin(); it != elements.rend(); ++it) {
215
109k
    const auto& element = *it;
216
109k
    stack->push(StackRecord(&element.expr()));
217
109k
  }
218
46.1k
}
219
220
void PushStructDeps(const StructExpr* struct_expr,
221
2.31k
                    std::stack<StackRecord>* stack) {
222
2.31k
  const auto& entries = struct_expr->fields();
223
4.02k
  for (auto it = entries.rbegin(); it != entries.rend(); ++it) {
224
1.71k
    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
1.71k
    if (entry.has_value()) {
228
1.71k
      stack->push(StackRecord(&entry.value()));
229
1.71k
    }
230
1.71k
  }
231
2.31k
}
232
233
4.86k
void PushMapDeps(const MapExpr* map_expr, std::stack<StackRecord>* stack) {
234
4.86k
  const auto& entries = map_expr->entries();
235
10.5k
  for (auto it = entries.rbegin(); it != entries.rend(); ++it) {
236
5.69k
    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
5.69k
    if (entry.has_value()) {
240
5.69k
      stack->push(StackRecord(&entry.value()));
241
5.69k
    }
242
    // The contract is to visit key, then value.  So put them on the stack
243
    // in the opposite order.
244
5.69k
    if (entry.has_key()) {
245
5.69k
      stack->push(StackRecord(&entry.key()));
246
5.69k
    }
247
5.69k
  }
248
4.86k
}
249
250
void PushComprehensionDeps(const ComprehensionExpr* c, const Expr* expr,
251
                           std::stack<StackRecord>* stack,
252
13.4k
                           bool use_comprehension_callbacks) {
253
13.4k
  StackRecord iter_range(&c->iter_range(), c, expr, ITER_RANGE,
254
13.4k
                         use_comprehension_callbacks);
255
13.4k
  StackRecord accu_init(&c->accu_init(), c, expr, ACCU_INIT,
256
13.4k
                        use_comprehension_callbacks);
257
13.4k
  StackRecord loop_condition(&c->loop_condition(), c, expr, LOOP_CONDITION,
258
13.4k
                             use_comprehension_callbacks);
259
13.4k
  StackRecord loop_step(&c->loop_step(), c, expr, LOOP_STEP,
260
13.4k
                        use_comprehension_callbacks);
261
13.4k
  StackRecord result(&c->result(), c, expr, RESULT,
262
13.4k
                     use_comprehension_callbacks);
263
  // Push them in reverse order.
264
13.4k
  stack->push(result);
265
13.4k
  stack->push(loop_step);
266
13.4k
  stack->push(loop_condition);
267
13.4k
  stack->push(accu_init);
268
13.4k
  stack->push(iter_range);
269
13.4k
}
270
271
struct PushDepsVisitor {
272
470k
  void operator()(const ExprRecord& record) {
273
470k
    struct {
274
470k
      std::stack<StackRecord>& stack;
275
470k
      const TraversalOptions& options;
276
470k
      const ExprRecord& record;
277
470k
      void operator()(const Constant& constant) {}
278
470k
      void operator()(const IdentExpr& ident) {}
279
470k
      void operator()(const SelectExpr& select) {
280
18.6k
        PushSelectDeps(&record.expr->select_expr(), &stack);
281
18.6k
      }
282
470k
      void operator()(const CallExpr& call) {
283
129k
        PushCallDeps(&record.expr->call_expr(), record.expr, &stack);
284
129k
      }
285
470k
      void operator()(const ListExpr& create_list) {
286
46.1k
        PushListDeps(&record.expr->list_expr(), &stack);
287
46.1k
      }
288
470k
      void operator()(const StructExpr& create_struct) {
289
2.31k
        PushStructDeps(&record.expr->struct_expr(), &stack);
290
2.31k
      }
291
470k
      void operator()(const MapExpr& map_expr) {
292
4.86k
        PushMapDeps(&record.expr->map_expr(), &stack);
293
4.86k
      }
294
470k
      void operator()(const ComprehensionExpr& comprehension) {
295
13.4k
        PushComprehensionDeps(&record.expr->comprehension_expr(), record.expr,
296
13.4k
                              &stack, options.use_comprehension_callbacks);
297
13.4k
      }
298
470k
      void operator()(const UnspecifiedExpr&) {}
299
470k
    } handler{stack, options, record};
300
470k
    absl::visit(handler, record.expr->kind());
301
470k
  }
302
303
244k
  void operator()(const ArgRecord& record) {
304
244k
    stack.push(StackRecord(record.expr));
305
244k
  }
306
307
67.3k
  void operator()(const ComprehensionRecord& record) {
308
67.3k
    stack.push(StackRecord(record.expr));
309
67.3k
  }
310
311
  std::stack<StackRecord>& stack;
312
  const TraversalOptions& options;
313
};
314
315
void PushDependencies(const StackRecord& record, std::stack<StackRecord>& stack,
316
782k
                      const TraversalOptions& options) {
317
782k
  absl::visit(PushDepsVisitor{stack, options}, record.record_variant);
318
782k
}
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
17.5k
                 TraversalOptions options) {
364
17.5k
  std::stack<StackRecord> stack;
365
17.5k
  stack.push(StackRecord(&expr));
366
367
1.58M
  while (!stack.empty()) {
368
1.56M
    StackRecord& record = stack.top();
369
1.56M
    if (!record.visited) {
370
782k
      PreVisit(record, &visitor);
371
782k
      PushDependencies(record, stack, options);
372
782k
      record.visited = true;
373
782k
    } else {
374
782k
      PostVisit(record, &visitor);
375
782k
      stack.pop();
376
782k
    }
377
1.56M
  }
378
17.5k
}
379
380
}  // namespace cel