Skip to content

Commit 769b0b1

Browse files
jnthntatumcopybara-github
authored andcommitted
Add non-recursive FlattenedErase for Expr.
PiperOrigin-RevId: 850184035
1 parent 457a5b5 commit 769b0b1

4 files changed

Lines changed: 223 additions & 6 deletions

File tree

common/BUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ cc_library(
5454
"@com_google_absl//absl/base:core_headers",
5555
"@com_google_absl//absl/base:no_destructor",
5656
"@com_google_absl//absl/functional:overload",
57+
"@com_google_absl//absl/log:absl_check",
5758
"@com_google_absl//absl/strings:string_view",
58-
"@com_google_absl//absl/types:optional",
5959
"@com_google_absl//absl/types:span",
6060
"@com_google_absl//absl/types:variant",
6161
],

common/expr.cc

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "absl/base/no_destructor.h"
2121
#include "absl/functional/overload.h"
22+
#include "absl/log/absl_check.h"
2223
#include "absl/types/variant.h"
2324
#include "common/constant.h"
2425

@@ -201,4 +202,119 @@ Expr& Expr::operator=(const Expr& other) {
201202

202203
Expr::Expr(const Expr& other) { CloneImpl(other, *this); }
203204

205+
namespace common_internal {
206+
struct ExprEraseTag {};
207+
} // namespace common_internal
208+
209+
namespace {
210+
void Expand(Expr** tail, Expr* cur) {
211+
static common_internal::ExprEraseTag tag;
212+
switch (cur->kind_case()) {
213+
case ExprKindCase::kSelectExpr: {
214+
SelectExpr& select = cur->mutable_select_expr();
215+
if (select.has_operand()) {
216+
select.mutable_operand().SetNext(tag, *tail);
217+
*tail = &select.mutable_operand();
218+
}
219+
break;
220+
}
221+
case ExprKindCase::kCallExpr: {
222+
CallExpr& call = cur->mutable_call_expr();
223+
if (call.has_target()) {
224+
call.mutable_target().SetNext(tag, *tail);
225+
*tail = &call.mutable_target();
226+
}
227+
for (auto& arg : call.mutable_args()) {
228+
arg.SetNext(tag, *tail);
229+
*tail = &arg;
230+
}
231+
break;
232+
}
233+
case ExprKindCase::kListExpr: {
234+
for (auto& arg : cur->mutable_list_expr().mutable_elements()) {
235+
arg.mutable_expr().SetNext(tag, *tail);
236+
*tail = &arg.mutable_expr();
237+
}
238+
break;
239+
}
240+
case ExprKindCase::kStructExpr: {
241+
for (auto& field : cur->mutable_struct_expr().mutable_fields()) {
242+
field.mutable_value().SetNext(tag, *tail);
243+
*tail = &field.mutable_value();
244+
}
245+
break;
246+
}
247+
case ExprKindCase::kMapExpr: {
248+
for (auto& entry : cur->mutable_map_expr().mutable_entries()) {
249+
entry.mutable_key().SetNext(tag, *tail);
250+
*tail = &entry.mutable_key();
251+
entry.mutable_value().SetNext(tag, *tail);
252+
*tail = &entry.mutable_value();
253+
}
254+
break;
255+
}
256+
case ExprKindCase::kComprehensionExpr: {
257+
if (cur->comprehension_expr().has_accu_init()) {
258+
cur->mutable_comprehension_expr().mutable_accu_init().SetNext(tag,
259+
*tail);
260+
*tail = &cur->mutable_comprehension_expr().mutable_accu_init();
261+
}
262+
if (cur->comprehension_expr().has_iter_range()) {
263+
cur->mutable_comprehension_expr().mutable_iter_range().SetNext(tag,
264+
*tail);
265+
*tail = &cur->mutable_comprehension_expr().mutable_iter_range();
266+
}
267+
if (cur->comprehension_expr().has_loop_condition()) {
268+
cur->mutable_comprehension_expr().mutable_loop_condition().SetNext(
269+
tag, *tail);
270+
*tail = &cur->mutable_comprehension_expr().mutable_loop_condition();
271+
}
272+
if (cur->comprehension_expr().has_loop_step()) {
273+
cur->mutable_comprehension_expr().mutable_loop_step().SetNext(tag,
274+
*tail);
275+
*tail = &cur->mutable_comprehension_expr().mutable_loop_step();
276+
}
277+
if (cur->comprehension_expr().has_result()) {
278+
cur->mutable_comprehension_expr().mutable_result().SetNext(tag, *tail);
279+
*tail = &cur->mutable_comprehension_expr().mutable_result();
280+
}
281+
break;
282+
}
283+
default:
284+
// Leaf node, nothing to expand.
285+
// Also a fallback in case we add a new node type.
286+
// Note: already in the deleter list so we can't delete now, will be
287+
// deleted after ordering the AST.
288+
break;
289+
}
290+
}
291+
} // namespace
292+
293+
void Expr::FlattenedErase() {
294+
// High level idea is to build a topological ordering of the AST, then erase
295+
// leaf to root.
296+
this->u_.next = nullptr;
297+
Expr* prev_tail = nullptr;
298+
Expr* tail = this;
299+
300+
while (tail != prev_tail) {
301+
Expr* next_prev_tail = tail;
302+
Expr* expand_ptr = tail;
303+
while (expand_ptr != prev_tail) {
304+
ABSL_DCHECK(expand_ptr != nullptr); // Linked list is broken or changed.
305+
Expr* next_expand_ptr = expand_ptr->u_.next;
306+
Expand(&tail, expand_ptr);
307+
expand_ptr = next_expand_ptr;
308+
}
309+
prev_tail = next_prev_tail;
310+
}
311+
312+
Expr* node = tail;
313+
while (node != nullptr) {
314+
Expr* next = node->u_.next;
315+
node->Clear();
316+
node = next;
317+
}
318+
}
319+
204320
} // namespace cel

common/expr.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,10 @@ enum class ExprKindCase {
884884
kComprehensionExpr,
885885
};
886886

887+
namespace common_internal {
888+
struct ExprEraseTag;
889+
} // namespace common_internal
890+
887891
// `Expr` is a node in the Common Expression Language's abstract syntax tree. It
888892
// is composed of a numeric ID and a kind variant.
889893
class Expr final {
@@ -897,9 +901,9 @@ class Expr final {
897901

898902
void Clear();
899903

900-
ABSL_MUST_USE_RESULT ExprId id() const { return id_; }
904+
ABSL_MUST_USE_RESULT ExprId id() const { return u_.id; }
901905

902-
void set_id(ExprId id) { id_ = id; }
906+
void set_id(ExprId id) { u_.id = id; }
903907

904908
ABSL_MUST_USE_RESULT const ExprKind& kind() const
905909
ABSL_ATTRIBUTE_LIFETIME_BOUND {
@@ -1071,6 +1075,11 @@ class Expr final {
10711075

10721076
friend void swap(Expr& lhs, Expr& rhs) noexcept;
10731077

1078+
// Erases the expr in place without recursion.
1079+
void FlattenedErase();
1080+
1081+
inline void SetNext(common_internal::ExprEraseTag&, Expr* next);
1082+
10741083
private:
10751084
friend class IdentExpr;
10761085
friend class SelectExpr;
@@ -1105,7 +1114,12 @@ class Expr final {
11051114
template <typename T>
11061115
ABSL_MUST_USE_RESULT T release_kind();
11071116

1108-
ExprId id_ = 0;
1117+
union {
1118+
ExprId id = 0;
1119+
// Intrusive pointer to the next element in the destructor chain.
1120+
// Only set from FlattenedErase.
1121+
Expr* next;
1122+
} u_;
11091123
ExprKind kind_;
11101124
};
11111125

@@ -1354,7 +1368,7 @@ inline std::vector<MapExprEntry> MapExpr::release_entries() {
13541368
}
13551369

13561370
inline void Expr::Clear() {
1357-
id_ = 0;
1371+
u_.id = 0;
13581372
mutable_kind().emplace<UnspecifiedExpr>();
13591373
}
13601374

@@ -1416,7 +1430,7 @@ inline ExprKindCase Expr::kind_case() const {
14161430

14171431
inline void swap(Expr& lhs, Expr& rhs) noexcept {
14181432
using std::swap;
1419-
swap(lhs.id_, rhs.id_);
1433+
swap(lhs.u_, rhs.u_);
14201434
swap(lhs.kind_, rhs.kind_);
14211435
}
14221436

@@ -1695,6 +1709,10 @@ inline Expr MapExprEntry::release(std::unique_ptr<Expr>& property) {
16951709
return Expr{};
16961710
}
16971711

1712+
inline void Expr::SetNext(common_internal::ExprEraseTag&, Expr* next) {
1713+
u_.next = next;
1714+
}
1715+
16981716
} // namespace cel
16991717

17001718
#endif // THIRD_PARTY_CEL_CPP_COMMON_EXPR_H_

common/expr_test.cc

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,89 @@ TEST(Expr, CopyAssignChildReference) {
580580
EXPECT_EQ(expr.call_expr().args()[1].ident_expr().name(), "qux");
581581
}
582582

583+
TEST(Expr, FlattenedErase) {
584+
Expr expr;
585+
auto& list_expr = expr.mutable_list_expr();
586+
list_expr.mutable_elements()
587+
.emplace_back()
588+
.mutable_expr()
589+
.mutable_ident_expr()
590+
.set_name("foo");
591+
592+
list_expr.mutable_elements()
593+
.emplace_back()
594+
.mutable_expr()
595+
.mutable_select_expr()
596+
.mutable_operand()
597+
.mutable_ident_expr()
598+
.set_name("foo");
599+
600+
auto& call_expr = list_expr.mutable_elements()
601+
.emplace_back()
602+
.mutable_expr()
603+
.mutable_call_expr();
604+
call_expr.set_function("foo");
605+
call_expr.mutable_target().mutable_ident_expr().set_name("bar");
606+
call_expr.mutable_args().emplace_back().mutable_ident_expr().set_name("baz");
607+
608+
auto& struct_expr = list_expr.mutable_elements()
609+
.emplace_back()
610+
.mutable_expr()
611+
.mutable_struct_expr();
612+
struct_expr.set_name("foo");
613+
auto& field = struct_expr.mutable_fields().emplace_back();
614+
field.set_name("bar");
615+
field.mutable_value().mutable_ident_expr().set_name("baz");
616+
617+
auto& map_expr = list_expr.mutable_elements()
618+
.emplace_back()
619+
.mutable_expr()
620+
.mutable_map_expr();
621+
auto& map_entry = map_expr.mutable_entries().emplace_back();
622+
map_entry.mutable_key().mutable_const_expr().set_string_value("foo");
623+
map_entry.mutable_value().mutable_ident_expr().set_name("bar");
624+
625+
auto& comprehension_expr = list_expr.mutable_elements()
626+
.emplace_back()
627+
.mutable_expr()
628+
.mutable_comprehension_expr();
629+
comprehension_expr.set_iter_var("foo");
630+
comprehension_expr.set_accu_var("bar");
631+
comprehension_expr.set_iter_range(Expr{});
632+
comprehension_expr.set_accu_init(Expr{});
633+
comprehension_expr.set_loop_condition(Expr{});
634+
comprehension_expr.set_loop_step(Expr{});
635+
comprehension_expr.set_result(Expr{});
636+
637+
expr.FlattenedErase();
638+
EXPECT_EQ(expr.kind_case(), ExprKindCase::kUnspecifiedExpr);
639+
}
640+
641+
Expr MakeNestedList(int size) {
642+
Expr e;
643+
Expr* node = &e;
644+
e.set_id(1);
645+
for (int i = 0; i < size; ++i) {
646+
node = &node->mutable_list_expr()
647+
.mutable_elements()
648+
.emplace_back()
649+
.mutable_expr();
650+
node->set_id(i + 2);
651+
}
652+
return e;
653+
}
654+
655+
TEST(Expr, FlattenedErase256k) {
656+
// Large expr to ensure we're not recursing. Would likely hit stack limits
657+
// with default destructor.
658+
constexpr int size = 256 * 1024;
659+
660+
Expr expr = MakeNestedList(size);
661+
662+
expr.FlattenedErase();
663+
EXPECT_EQ(expr.kind_case(), ExprKindCase::kUnspecifiedExpr);
664+
}
665+
583666
TEST(Expr, Id) {
584667
Expr expr;
585668
EXPECT_THAT(expr.id(), Eq(0));

0 commit comments

Comments
 (0)