4141import dev .cel .common .internal .DateTimeHelpers ;
4242import dev .cel .common .navigation .CelNavigableMutableAst ;
4343import dev .cel .common .navigation .CelNavigableMutableExpr ;
44+ import dev .cel .common .navigation .TraversalOrder ;
4445import dev .cel .common .types .SimpleType ;
4546import dev .cel .extensions .CelOptionalLibrary .Function ;
4647import dev .cel .optimizer .AstMutator ;
@@ -111,7 +112,7 @@ public OptimizationResult optimize(CelAbstractSyntaxTree ast, Cel cel)
111112 ImmutableList <CelNavigableMutableExpr > foldableExprs =
112113 CelNavigableMutableAst .fromAst (mutableAst )
113114 .getRoot ()
114- .allNodes ()
115+ .allNodes (TraversalOrder . PRE_ORDER )
115116 .filter (this ::canFold )
116117 .collect (toImmutableList ());
117118 for (CelNavigableMutableExpr foldableExpr : foldableExprs ) {
@@ -122,7 +123,13 @@ public OptimizationResult optimize(CelAbstractSyntaxTree ast, Cel cel)
122123 mutatedResult = maybePruneBranches (mutableAst , foldableExpr .expr ());
123124 if (!mutatedResult .isPresent ()) {
124125 // Evaluate the call then fold
125- mutatedResult = maybeFold (optimizerEnv , mutableAst , foldableExpr );
126+ try {
127+ mutatedResult = maybeFold (optimizerEnv , mutableAst , foldableExpr );
128+ } catch (CelEvaluationException e ) {
129+ throw new CelOptimizationException (
130+ "Constant folding failure. Failed to evaluate subtree due to: " + e .getMessage (),
131+ e );
132+ }
126133 }
127134
128135 if (!mutatedResult .isPresent ()) {
@@ -132,12 +139,17 @@ public OptimizationResult optimize(CelAbstractSyntaxTree ast, Cel cel)
132139
133140 continueFolding = true ;
134141 mutableAst = mutatedResult .get ();
142+ // Break the loop because we mutated the AST. Since we traverse in PRE_ORDER (top-down),
143+ // mutating a parent node means its children are now obsolete or folded.
144+ // We restart the traversal to gather a fresh list of foldable expressions.
145+ break ;
135146 }
136147 }
137148
138149 // If the output is a list, map, or struct which contains optional entries, then prune it
139150 // to make sure that the optionals, if resolved, do not surface in the output literal.
140151 mutableAst = pruneOptionalElements (mutableAst );
152+
141153 return OptimizationResult .create (astMutator .renumberIdsConsecutively (mutableAst ).toParsedAst ());
142154 }
143155
@@ -280,11 +292,11 @@ private static boolean isNestedComprehension(CelNavigableMutableExpr expr) {
280292
281293 private Optional <CelMutableAst > maybeFold (
282294 Cel cel , CelMutableAst mutableAst , CelNavigableMutableExpr node )
283- throws CelOptimizationException {
295+ throws CelOptimizationException , CelEvaluationException {
284296 Object result ;
285297 try {
286298 result = evaluateExpr (cel , node );
287- } catch (CelValidationException | CelEvaluationException e ) {
299+ } catch (CelValidationException e ) {
288300 throw new CelOptimizationException (
289301 "Constant folding failure. Failed to evaluate subtree due to: " + e .getMessage (), e );
290302 }
0 commit comments