Coverage Report

Created: 2026-07-16 06:29

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