Coverage Report

Created: 2026-07-11 06:47

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