Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 27 additions & 39 deletions optimizer/src/main/java/dev/cel/optimizer/AstMutator.java
Original file line number Diff line number Diff line change
Expand Up @@ -677,55 +677,43 @@ private CelMutableExpr mangleIdentsInComprehensionExpr(
String originalAccuVar,
MangledComprehensionName mangledComprehensionName) {
CelMutableComprehension comprehension = comprehensionExpr.comprehension();
replaceIdentName(
comprehension.loopStep(), originalIterVar, mangledComprehensionName.iterVarName());
replaceIdentName(comprehensionExpr, originalAccuVar, mangledComprehensionName.resultName());

// Iteration variables are in scope of the loop condition and step, the accumulator also of the
// result. Neither is in scope of the iteration range or the accumulator initializer.
renameInLoop(comprehension, originalIterVar, mangledComprehensionName.iterVarName());
comprehension.setIterVar(mangledComprehensionName.iterVarName());

// Most standard macros set accu_var as @result, but not all (ex: cel.bind).
if (comprehension.accuVar().equals(originalAccuVar)) {
comprehension.setAccuVar(mangledComprehensionName.resultName());
}

if (!originalIterVar2.isEmpty()) {
renameInLoop(comprehension, originalIterVar2, mangledComprehensionName.iterVar2Name());
comprehension.setIterVar2(mangledComprehensionName.iterVar2Name());
replaceIdentName(
comprehension.loopStep(), originalIterVar2, mangledComprehensionName.iterVar2Name());
}
String resultName = mangledComprehensionName.resultName();
renameInLoop(comprehension, originalAccuVar, resultName);
comprehension.setResult(replaceIdentName(comprehension.result(), originalAccuVar, resultName));
comprehension.setAccuVar(resultName);

return mutateExpr(NO_OP_ID_GENERATOR, root, comprehensionExpr, comprehensionExpr.id());
}

private void replaceIdentName(
CelMutableExpr comprehensionExpr, String originalIdentName, String newIdentName) {
int iterCount;
for (iterCount = 0; iterCount < iterationLimit; iterCount++) {
CelMutableExpr identToMangle =
CelNavigableMutableExpr.fromExpr(comprehensionExpr)
.descendants()
.map(CelNavigableMutableExpr::expr)
.filter(
node ->
node.getKind().equals(ExprKind.Kind.IDENT)
&& node.ident().name().equals(originalIdentName))
.findAny()
.orElse(null);
if (identToMangle == null) {
break;
}

comprehensionExpr =
mutateExpr(
NO_OP_ID_GENERATOR,
comprehensionExpr,
CelMutableExpr.ofIdent(newIdentName),
identToMangle.id());
}
private void renameInLoop(CelMutableComprehension comprehension, String from, String to) {
comprehension.setLoopCondition(replaceIdentName(comprehension.loopCondition(), from, to));
comprehension.setLoopStep(replaceIdentName(comprehension.loopStep(), from, to));
}

if (iterCount >= iterationLimit) {
throw new IllegalStateException("Max iteration count reached.");
// Returns the renamed expression, which is a new node if `expr` itself is a matching identifier.
private CelMutableExpr replaceIdentName(
CelMutableExpr expr, String originalIdentName, String newIdentName) {
ImmutableList<Long> identIds =
CelNavigableMutableExpr.fromExpr(expr)
.allNodes()
.filter(
node ->
node.getKind().equals(ExprKind.Kind.IDENT)
&& node.expr().ident().name().equals(originalIdentName))
.map(CelNavigableMutableExpr::id)
.collect(toImmutableList());
for (long id : identIds) {
expr = mutateExpr(NO_OP_ID_GENERATOR, expr, CelMutableExpr.ofIdent(newIdentName), id);
}
return expr;
}

private CelMutableSource mangleIdentsInMacroSource(
Expand Down
16 changes: 16 additions & 0 deletions optimizer/src/test/java/dev/cel/optimizer/AstMutatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,22 @@ public void mangleComprehensionVariable_nonIdentComprehensionResult() throws Exc
assertConsistentMacroCalls(mangledAst);
}

@Test
public void mangleComprehensionVariable_bindInitReferencesOuterVariable() throws Exception {
// The `x` in the initializer refers to the outer variable, not to the one being bound.
CelAbstractSyntaxTree ast = CEL.compile("cel.bind(x, x, x + 1)").getAst();

CelAbstractSyntaxTree mangledAst =
AST_MUTATOR
.mangleComprehensionIdentifierNames(CelMutableAst.fromCelAst(ast), "@it", "@it2", "@ac")
.mutableAst()
.toParsedAst();

assertThat(mangledAst.getExpr().comprehension().loopStep().ident().name()).isEqualTo("@ac:0:0");
assertThat(CEL.createProgram(CEL.check(mangledAst).getAst()).eval(ImmutableMap.of("x", 5L)))
.isEqualTo(6L);
}

@Test
public void mangleComprehensionVariable_macroSourceDisabled_macroCallMapIsEmpty()
throws Exception {
Expand Down
Loading