Skip to content

Commit 681f99f

Browse files
Cristina Borzacopybara-github
authored andcommitted
Fix a bug in the assertAstIdCorrectness method and avoid re-checking the AST in CelOptimizer if it was not modified.
PiperOrigin-RevId: 976199136
1 parent d374a0d commit 681f99f

2 files changed

Lines changed: 90 additions & 19 deletions

File tree

optimizer/src/main/java/dev/cel/optimizer/CelOptimizerImpl.java

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package dev.cel.optimizer;
1616

1717
import static com.google.common.base.Preconditions.checkNotNull;
18+
import static com.google.common.base.Preconditions.checkState;
1819

1920
import com.google.common.collect.ImmutableSet;
2021
import dev.cel.bundle.Cel;
@@ -44,6 +45,7 @@ final class CelOptimizerImpl implements CelOptimizer {
4445
}
4546

4647
@Override
48+
@SuppressWarnings("ReferenceEquality")
4749
public CelAbstractSyntaxTree optimize(CelAbstractSyntaxTree ast) throws CelOptimizationException {
4850
if (!ast.isChecked()) {
4951
throw new IllegalArgumentException("AST must be type-checked.");
@@ -64,16 +66,18 @@ public CelAbstractSyntaxTree optimize(CelAbstractSyntaxTree ast) throws CelOptim
6466

6567
OptimizationResult result = optimizer.optimize(optimizedAst, celOptimizerEnv);
6668

67-
if (!result.newFunctionDecls().isEmpty() || !result.newVarDecls().isEmpty()) {
68-
celOptimizerEnv =
69-
celOptimizerEnv
70-
.toCelBuilder()
71-
.addVarDeclarations(result.newVarDecls())
72-
.addFunctionDeclarations(result.newFunctionDecls())
73-
.build();
69+
if (result.optimizedAst() != optimizedAst) {
70+
if (!result.newFunctionDecls().isEmpty() || !result.newVarDecls().isEmpty()) {
71+
celOptimizerEnv =
72+
celOptimizerEnv
73+
.toCelBuilder()
74+
.addVarDeclarations(result.newVarDecls())
75+
.addFunctionDeclarations(result.newFunctionDecls())
76+
.build();
77+
}
78+
optimizedAst = celOptimizerEnv.check(result.optimizedAst()).getAst();
79+
assertAstIdCorrectness(optimizedAst);
7480
}
75-
optimizedAst = celOptimizerEnv.check(result.optimizedAst()).getAst();
76-
assertAstIdCorrectness(optimizedAst);
7781

7882
for (CelOptimizerListener listener : listeners) {
7983
listener.onPassEnd(optimizer, preAst, optimizedAst);
@@ -131,19 +135,26 @@ private static void assertAstIdCorrectness(CelAbstractSyntaxTree ast) {
131135
return;
132136
}
133137

134-
if (astExpr.exprKind().getKind().equals(Kind.COMPREHENSION)) {
135-
if (!macroExpr.exprKind().getKind().equals(Kind.NOT_SET)) {
136-
throw new IllegalStateException(
137-
String.format(
138-
"Expected macro call node %d to be NOT_SET for comprehension, but"
139-
+ " was %s.",
140-
macroExpr.id(), macroExpr.exprKind().getKind()));
141-
}
138+
if (macroExpr.exprKind().getKind().equals(Kind.NOT_SET)) {
139+
// If a macro node is NOT_SET, its ID must be present in the main AST.
140+
checkState(
141+
ast.getSource().getMacroCalls().containsKey(macroExpr.id()),
142+
"Expected macro call node %s to be present in macro calls map, but was not.",
143+
macroExpr.id());
144+
} else if (astExpr.exprKind().getKind().equals(Kind.COMPREHENSION)) {
145+
// We encountered something other than NOT_SET in macro source for comprehension
146+
// node. This is an error.
147+
throw new IllegalStateException(
148+
String.format(
149+
"Expected macro call node %d to be NOT_SET for comprehension, but was"
150+
+ " %s.",
151+
macroExpr.id(), macroExpr.exprKind().getKind()));
142152
} else if (!macroExpr.exprKind().getKind().equals(astExpr.exprKind().getKind())) {
153+
// Otherwise for all cases, the AST node should match exactly.
143154
throw new IllegalStateException(
144155
String.format(
145-
"Macro call node %d kind mismatch: expected %s (from AST), but was %s"
146-
+ " (in macro call).",
156+
"Macro call node %d kind mismatch: expected %s (from AST), but was %s (in"
157+
+ " macro call).",
147158
macroExpr.id(),
148159
astExpr.exprKind().getKind(),
149160
macroExpr.exprKind().getKind()));

optimizer/src/test/java/dev/cel/optimizer/optimizers/SubexpressionOptimizerTest.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,66 @@ public void block_lazyEvaluationContainsError_cleansUpCycleState() throws Except
702702
assertThat(e).hasMessageThat().doesNotContain("Cycle detected");
703703
}
704704

705+
@Test
706+
public void cse_nestedMacro_noOp_assertAstIdCorrectness() throws Exception {
707+
Cel cel =
708+
runtimeFlavor
709+
.builder()
710+
.addVar("x", SimpleType.DYN)
711+
.setStandardMacros(CelStandardMacro.STANDARD_MACROS)
712+
.setOptions(CelOptions.current().populateMacroCalls(true).build())
713+
.addCompilerLibraries(CelExtensions.comprehensions())
714+
.addRuntimeLibraries(CelExtensions.comprehensions())
715+
.build();
716+
CelOptimizer celOptimizer =
717+
CelOptimizerFactory.standardCelOptimizerBuilder(cel)
718+
.addAstOptimizers(SubexpressionOptimizer.getInstance())
719+
.build();
720+
CelAbstractSyntaxTree ast =
721+
cel.compile("[{}, {\"a\": 1}, {\"b\": 2}].filter(m, has(x.a))").getAst();
722+
723+
CelAbstractSyntaxTree optimizedAst = celOptimizer.optimize(ast);
724+
725+
assertThat(CEL_UNPARSER.unparse(optimizedAst))
726+
.isEqualTo("[{}, {\"a\": 1}, {\"b\": 2}].filter(m, has(x.a))");
727+
assertThat(optimizedAst).isSameInstanceAs(ast);
728+
}
729+
730+
@Test
731+
public void cse_nestedMacro_withOptimization_assertAstIdCorrectness() throws Exception {
732+
Cel cel =
733+
runtimeFlavor
734+
.builder()
735+
.addVar("x", SimpleType.DYN)
736+
.setStandardMacros(CelStandardMacro.STANDARD_MACROS)
737+
.setOptions(
738+
CelOptions.current()
739+
.populateMacroCalls(true)
740+
.enableHeterogeneousNumericComparisons(true)
741+
.build())
742+
.addCompilerLibraries(CelExtensions.comprehensions())
743+
.addRuntimeLibraries(CelExtensions.comprehensions())
744+
.build();
745+
CelOptimizer celOptimizer =
746+
CelOptimizerFactory.standardCelOptimizerBuilder(cel)
747+
.addAstOptimizers(
748+
SubexpressionOptimizer.newInstance(
749+
SubexpressionOptimizerOptions.newBuilder().populateMacroCalls(true).build()))
750+
.build();
751+
CelAbstractSyntaxTree ast =
752+
cel.compile(
753+
"[{}, {\"a\": 1}, {\"b\": 2}].filter(m, has(x.a)) == [{}, {\"a\": 1}, {\"b\":"
754+
+ " 2}].filter(m, has(x.a))")
755+
.getAst();
756+
757+
CelAbstractSyntaxTree optimizedAst = celOptimizer.optimize(ast);
758+
759+
assertThat(CEL_UNPARSER.unparse(optimizedAst))
760+
.isEqualTo(
761+
"cel.@block([[{}, {\"a\": 1}, {\"b\": 2}].filter(@it:0:0, has(x.a))], @index0 =="
762+
+ " @index0)");
763+
}
764+
705765
/**
706766
* Converts AST containing cel.block related test functions to internal functions (e.g: cel.block
707767
* -> cel.@block)

0 commit comments

Comments
 (0)