Skip to content

Commit de7f519

Browse files
l46kokcopybara-github
authored andcommitted
Remove cel.bind option from SubexpressionOptimizer
PiperOrigin-RevId: 797496424
1 parent fe46a63 commit de7f519

23 files changed

Lines changed: 349 additions & 592 deletions

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -910,21 +910,32 @@ private static void unwrapListArgumentsInMacroCallExpr(
910910

911911
CelMutableExpr loopStepExpr = comprehension.loopStep();
912912
List<CelMutableExpr> loopStepArgs = loopStepExpr.call().args();
913-
if (loopStepArgs.size() != 2) {
913+
if (loopStepArgs.size() != 2 && loopStepArgs.size() != 3) {
914914
throw new IllegalArgumentException(
915915
String.format(
916-
"Expected exactly 2 arguments but got %d instead on expr id: %d",
916+
"Expected exactly 2 or 3 arguments but got %d instead on expr id: %d",
917917
loopStepArgs.size(), loopStepExpr.id()));
918918
}
919919

920920
CelMutableCall existingMacroCall = newMacroCallExpr.call();
921921
CelMutableCall newMacroCall =
922922
existingMacroCall.target().isPresent()
923-
? CelMutableCall.create(existingMacroCall.target().get(), existingMacroCall.function())
923+
? CelMutableCall.create(existingMacroCall.target().get(),
924+
existingMacroCall.function())
924925
: CelMutableCall.create(existingMacroCall.function());
925926
newMacroCall.addArgs(
926927
existingMacroCall.args().get(0)); // iter_var is first argument of the call by convention
927-
newMacroCall.addArgs(loopStepArgs.get(1).list().elements());
928+
929+
CelMutableList extraneousList = null;
930+
if (loopStepArgs.size() == 2) {
931+
extraneousList = loopStepArgs.get(1).list();
932+
} else {
933+
newMacroCall.addArgs(loopStepArgs.get(0));
934+
// For map(x,y,z), z is wrapped in a _+_(@result, [z])
935+
extraneousList = loopStepArgs.get(1).call().args().get(1).list();
936+
}
937+
938+
newMacroCall.addArgs(extraneousList.elements());
928939

929940
newMacroCallExpr.setCall(newMacroCall);
930941
}

optimizer/src/main/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,9 @@ private static boolean canFoldInOperator(CelNavigableMutableExpr navigableExpr)
202202
CelNavigableMutableExpr parent = identNode.parent().orElse(null);
203203
while (parent != null) {
204204
if (parent.getKind().equals(Kind.COMPREHENSION)) {
205-
if (parent.expr().comprehension().accuVar().equals(identNode.expr().ident().name())) {
205+
String identName = identNode.expr().ident().name();
206+
if (parent.expr().comprehension().accuVar().equals(identName)
207+
|| parent.expr().comprehension().iterVar().equals(identName)) {
206208
// Prevent folding a subexpression if it contains a variable declared by a
207209
// comprehension. The subexpression cannot be compiled without the full context of the
208210
// surrounding comprehension.

optimizer/src/main/java/dev/cel/optimizer/optimizers/SubexpressionOptimizer.java

Lines changed: 8 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,8 @@
5757
import java.util.ArrayList;
5858
import java.util.Arrays;
5959
import java.util.Comparator;
60-
import java.util.HashMap;
6160
import java.util.HashSet;
6261
import java.util.List;
63-
import java.util.Optional;
6462
import java.util.Set;
6563

6664
/**
@@ -121,8 +119,7 @@ public static SubexpressionOptimizer newInstance(SubexpressionOptimizerOptions c
121119

122120
@Override
123121
public OptimizationResult optimize(CelAbstractSyntaxTree ast, Cel cel) {
124-
OptimizationResult result =
125-
cseOptions.enableCelBlock() ? optimizeUsingCelBlock(ast, cel) : optimizeUsingCelBind(ast);
122+
OptimizationResult result = optimizeUsingCelBlock(ast, cel);
126123

127124
verifyOptimizedAstCorrectness(result.optimizedAst());
128125

@@ -184,7 +181,7 @@ private OptimizationResult optimizeUsingCelBlock(CelAbstractSyntaxTree ast, Cel
184181

185182
if (iterCount == 0) {
186183
// No modification has been made.
187-
return OptimizationResult.create(astToModify.toParsedAst());
184+
return OptimizationResult.create(ast);
188185
}
189186

190187
ImmutableList.Builder<CelVarDecl> newVarDecls = ImmutableList.builder();
@@ -330,123 +327,8 @@ private static ImmutableList<CelVarDecl> newBlockIndexVariableDeclarations(
330327
return varDeclBuilder.build();
331328
}
332329

333-
private OptimizationResult optimizeUsingCelBind(CelAbstractSyntaxTree ast) {
334-
CelMutableAst astToModify = CelMutableAst.fromCelAst(ast);
335-
if (!cseOptions.populateMacroCalls()) {
336-
astToModify.source().clearMacroCalls();
337-
}
338-
339-
astToModify =
340-
astMutator
341-
.mangleComprehensionIdentifierNames(
342-
astToModify,
343-
MANGLED_COMPREHENSION_ITER_VAR_PREFIX,
344-
MANGLED_COMPREHENSION_ACCU_VAR_PREFIX,
345-
/* incrementSerially= */ true)
346-
.mutableAst();
347-
CelMutableSource sourceToModify = astToModify.source();
348-
349-
int bindIdentifierIndex = 0;
350-
int iterCount;
351-
for (iterCount = 0; iterCount < cseOptions.iterationLimit(); iterCount++) {
352-
CelNavigableMutableAst navAst = CelNavigableMutableAst.fromAst(astToModify);
353-
List<CelMutableExpr> cseCandidates = getCseCandidates(navAst);
354-
if (cseCandidates.isEmpty()) {
355-
break;
356-
}
357-
358-
String bindIdentifier = BIND_IDENTIFIER_PREFIX + bindIdentifierIndex;
359-
bindIdentifierIndex++;
360-
361-
// Replace all CSE candidates with new bind identifier
362-
for (CelMutableExpr cseCandidate : cseCandidates) {
363-
iterCount++;
364-
365-
astToModify =
366-
astMutator.replaceSubtree(
367-
astToModify, CelMutableExpr.ofIdent(bindIdentifier), cseCandidate.id());
368-
}
369-
370-
// Find LCA to insert the new cel.bind macro into.
371-
CelNavigableMutableExpr lca = getLca(navAst, bindIdentifier);
372-
373-
// Insert the new bind call
374-
CelMutableExpr subexpressionToBind = cseCandidates.get(0);
375-
// Re-add the macro source for bind identifiers that might have been lost from previous
376-
// iteration of CSE
377-
astToModify.source().addAllMacroCalls(sourceToModify.getMacroCalls());
378-
astToModify =
379-
astMutator.replaceSubtreeWithNewBindMacro(
380-
astToModify,
381-
bindIdentifier,
382-
subexpressionToBind,
383-
lca.expr(),
384-
lca.id(),
385-
cseOptions.populateMacroCalls());
386-
387-
// Retain the existing macro calls in case if the bind identifiers are replacing a subtree
388-
// that contains a comprehension.
389-
sourceToModify = astToModify.source();
390-
}
391-
392-
if (iterCount >= cseOptions.iterationLimit()) {
393-
throw new IllegalStateException("Max iteration count reached.");
394-
}
395-
396-
if (iterCount == 0) {
397-
// No modification has been made.
398-
return OptimizationResult.create(astToModify.toParsedAst());
399-
}
400-
401-
astToModify = astMutator.renumberIdsConsecutively(astToModify);
402-
403-
return OptimizationResult.create(astToModify.toParsedAst());
404-
}
405-
406-
private static CelNavigableMutableExpr getLca(
407-
CelNavigableMutableAst navAst, String boundIdentifier) {
408-
CelNavigableMutableExpr root = navAst.getRoot();
409-
ImmutableList<CelNavigableMutableExpr> allNodesWithIdentifier =
410-
root.allNodes()
411-
.filter(
412-
node ->
413-
node.getKind().equals(Kind.IDENT)
414-
&& node.expr().ident().name().equals(boundIdentifier))
415-
.collect(toImmutableList());
416-
417-
if (allNodesWithIdentifier.size() < 2) {
418-
throw new IllegalStateException("Expected at least 2 bound identifiers to be present.");
419-
}
420-
421-
CelNavigableMutableExpr lca = root;
422-
long lcaAncestorCount = 0;
423-
HashMap<Long, Long> ancestors = new HashMap<>();
424-
for (CelNavigableMutableExpr navigableExpr : allNodesWithIdentifier) {
425-
Optional<CelNavigableMutableExpr> maybeParent = Optional.of(navigableExpr);
426-
while (maybeParent.isPresent()) {
427-
CelNavigableMutableExpr parent = maybeParent.get();
428-
if (!ancestors.containsKey(parent.id())) {
429-
ancestors.put(parent.id(), 1L);
430-
continue;
431-
}
432-
433-
long ancestorCount = ancestors.get(parent.id());
434-
if (lcaAncestorCount < ancestorCount
435-
|| (lcaAncestorCount == ancestorCount && lca.depth() < parent.depth())) {
436-
lca = parent;
437-
lcaAncestorCount = ancestorCount;
438-
}
439-
440-
ancestors.put(parent.id(), ancestorCount + 1);
441-
maybeParent = parent.parent();
442-
}
443-
}
444-
445-
return lca;
446-
}
447-
448330
private List<CelMutableExpr> getCseCandidates(CelNavigableMutableAst navAst) {
449-
if (cseOptions.enableCelBlock() && cseOptions.subexpressionMaxRecursionDepth() > 0) {
331+
if (cseOptions.subexpressionMaxRecursionDepth() > 0) {
450332
return getCseCandidatesWithRecursionDepth(
451333
navAst, cseOptions.subexpressionMaxRecursionDepth());
452334
} else {
@@ -689,11 +571,9 @@ public abstract static class Builder {
689571
public abstract Builder populateMacroCalls(boolean value);
690572

691573
/**
692-
* Rewrites the optimized AST using cel.@block call instead of cascaded cel.bind macros, aimed
693-
* to produce a more compact AST. {@link CelSource.Extension} field will be populated in the
694-
* AST to inform that special runtime support is required to evaluate the optimized
695-
* expression.
574+
* @deprecated This option is a no-op. cel.@block is always enabled.
696575
*/
576+
@Deprecated
697577
public abstract Builder enableCelBlock(boolean value);
698578

699579
/**
@@ -708,9 +588,8 @@ public abstract static class Builder {
708588
* <p>Note that expressions containing no common subexpressions may become a candidate for
709589
* extraction to satisfy the max depth requirement.
710590
*
711-
* <p>This is a no-op if {@link #enableCelBlock} is set to false, the configured value is less
712-
* than 1, or no subexpression needs to be extracted because the entire expression is already
713-
* under the designated limit.
591+
* <p>This is a no-op if the configured value is less than 1, or no subexpression needs to be
592+
* extracted because the entire expression is already under the designated limit.
714593
*
715594
* <p>Examples:
716595
*
@@ -756,8 +635,8 @@ public Builder addEliminableFunctions(String... functions) {
756635
public static Builder newBuilder() {
757636
return new AutoValue_SubexpressionOptimizer_SubexpressionOptimizerOptions.Builder()
758637
.iterationLimit(500)
638+
.enableCelBlock(true)
759639
.populateMacroCalls(false)
760-
.enableCelBlock(false)
761640
.subexpressionMaxRecursionDepth(0);
762641
}
763642

optimizer/src/test/java/dev/cel/optimizer/AstMutatorTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,34 @@ public void replaceSubtree_replaceExtraneousListCreatedByMacro_unparseSuccess()
581581
.containsExactly(2L);
582582
}
583583

584+
@Test
585+
@SuppressWarnings("unchecked") // Test only
586+
public void replaceSubtree_replaceExtraneousListCreatedByThreeArgMacro_unparseSuccess()
587+
throws Exception {
588+
CelAbstractSyntaxTree ast = CEL.compile("[1].map(x, true, 1)").getAst();
589+
CelMutableAst mutableAst = CelMutableAst.fromCelAst(ast);
590+
CelMutableAst mutableAst2 = CelMutableAst.fromCelAst(ast);
591+
592+
// These two mutation are equivalent.
593+
CelAbstractSyntaxTree mutatedAstWithList =
594+
AST_MUTATOR
595+
.replaceSubtree(
596+
mutableAst,
597+
CelMutableExpr.ofList(
598+
CelMutableList.create(CelMutableExpr.ofConstant(CelConstant.ofValue(2L)))),
599+
10L)
600+
.toParsedAst();
601+
CelAbstractSyntaxTree mutatedAstWithConstant =
602+
AST_MUTATOR
603+
.replaceSubtree(mutableAst2, CelMutableExpr.ofConstant(CelConstant.ofValue(2L)), 6L)
604+
.toParsedAst();
605+
606+
assertThat(CEL_UNPARSER.unparse(mutatedAstWithList)).isEqualTo("[1].map(x, true, 2)");
607+
assertThat(CEL_UNPARSER.unparse(mutatedAstWithConstant)).isEqualTo("[1].map(x, true, 2)");
608+
assertThat((List<Long>) CEL.createProgram(CEL.check(mutatedAstWithList).getAst()).eval())
609+
.containsExactly(2L);
610+
}
611+
584612
@Test
585613
public void globalCallExpr_replaceRoot() throws Exception {
586614
// Tree shape (brackets are expr IDs):

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

Lines changed: 1 addition & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ public class SubexpressionOptimizerBaselineTest extends BaselineTestCase {
7373
private static final SubexpressionOptimizerOptions OPTIMIZER_COMMON_OPTIONS =
7474
SubexpressionOptimizerOptions.newBuilder()
7575
.populateMacroCalls(true)
76-
.enableCelBlock(true)
7776
.addEliminableFunctions("pure_custom_func")
7877
.build();
7978

@@ -187,67 +186,11 @@ public void subexpression_ast(@TestParameter CseTestOptimizer cseTestOptimizer)
187186
}
188187
}
189188

190-
@Test
191-
public void populateMacroCallsDisabled_macroMapUnpopulated(@TestParameter CseTestCase testCase)
192-
throws Exception {
193-
skipBaselineVerification();
194-
Cel cel = newCelBuilder().build();
195-
CelOptimizer celOptimizerWithBinds =
196-
newCseOptimizer(
197-
cel,
198-
SubexpressionOptimizerOptions.newBuilder()
199-
.populateMacroCalls(false)
200-
.enableCelBlock(false)
201-
.build());
202-
CelOptimizer celOptimizerWithBlocks =
203-
newCseOptimizer(
204-
cel,
205-
SubexpressionOptimizerOptions.newBuilder()
206-
.populateMacroCalls(false)
207-
.enableCelBlock(true)
208-
.build());
209-
CelOptimizer celOptimizerWithFlattenedBlocks =
210-
newCseOptimizer(
211-
cel,
212-
SubexpressionOptimizerOptions.newBuilder()
213-
.populateMacroCalls(false)
214-
.enableCelBlock(true)
215-
.subexpressionMaxRecursionDepth(1)
216-
.build());
217-
CelAbstractSyntaxTree originalAst = cel.compile(testCase.source).getAst();
218-
219-
CelAbstractSyntaxTree astOptimizedWithBinds = celOptimizerWithBinds.optimize(originalAst);
220-
CelAbstractSyntaxTree astOptimizedWithBlocks = celOptimizerWithBlocks.optimize(originalAst);
221-
CelAbstractSyntaxTree astOptimizedWithFlattenedBlocks =
222-
celOptimizerWithFlattenedBlocks.optimize(originalAst);
223-
224-
assertThat(astOptimizedWithBinds.getSource().getMacroCalls()).isEmpty();
225-
assertThat(astOptimizedWithBlocks.getSource().getMacroCalls()).isEmpty();
226-
assertThat(astOptimizedWithFlattenedBlocks.getSource().getMacroCalls()).isEmpty();
227-
}
228-
229-
@Test
230-
public void large_expressions_bind_cascaded() throws Exception {
231-
CelOptimizer celOptimizer =
232-
newCseOptimizer(
233-
CEL,
234-
SubexpressionOptimizerOptions.newBuilder()
235-
.populateMacroCalls(true)
236-
.enableCelBlock(false)
237-
.build());
238-
239-
runLargeTestCases(celOptimizer);
240-
}
241-
242189
@Test
243190
public void large_expressions_block_common_subexpr() throws Exception {
244191
CelOptimizer celOptimizer =
245192
newCseOptimizer(
246-
CEL,
247-
SubexpressionOptimizerOptions.newBuilder()
248-
.populateMacroCalls(true)
249-
.enableCelBlock(true)
250-
.build());
193+
CEL, SubexpressionOptimizerOptions.newBuilder().populateMacroCalls(true).build());
251194

252195
runLargeTestCases(celOptimizer);
253196
}
@@ -259,7 +202,6 @@ public void large_expressions_block_recursion_depth_1() throws Exception {
259202
CEL,
260203
SubexpressionOptimizerOptions.newBuilder()
261204
.populateMacroCalls(true)
262-
.enableCelBlock(true)
263205
.subexpressionMaxRecursionDepth(1)
264206
.build());
265207

@@ -273,7 +215,6 @@ public void large_expressions_block_recursion_depth_2() throws Exception {
273215
CEL,
274216
SubexpressionOptimizerOptions.newBuilder()
275217
.populateMacroCalls(true)
276-
.enableCelBlock(true)
277218
.subexpressionMaxRecursionDepth(2)
278219
.build());
279220

@@ -287,7 +228,6 @@ public void large_expressions_block_recursion_depth_3() throws Exception {
287228
CEL,
288229
SubexpressionOptimizerOptions.newBuilder()
289230
.populateMacroCalls(true)
290-
.enableCelBlock(true)
291231
.subexpressionMaxRecursionDepth(3)
292232
.build());
293233

@@ -351,7 +291,6 @@ private static CelOptimizer newCseOptimizer(Cel cel, SubexpressionOptimizerOptio
351291

352292
@SuppressWarnings("Immutable") // Test only
353293
private enum CseTestOptimizer {
354-
CASCADED_BINDS(OPTIMIZER_COMMON_OPTIONS.toBuilder().enableCelBlock(false).build()),
355294
BLOCK_COMMON_SUBEXPR_ONLY(OPTIMIZER_COMMON_OPTIONS),
356295
BLOCK_RECURSION_DEPTH_1(
357296
OPTIMIZER_COMMON_OPTIONS.toBuilder().subexpressionMaxRecursionDepth(1).build()),

0 commit comments

Comments
 (0)