Skip to content

Commit d07d536

Browse files
committed
Block index fix
1 parent 8b4c579 commit d07d536

2 files changed

Lines changed: 43 additions & 2 deletions

File tree

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import dev.cel.parser.CelStandardMacro;
5353
import dev.cel.parser.CelUnparser;
5454
import dev.cel.parser.CelUnparserFactory;
55+
import dev.cel.runtime.CelEvaluationException;
5556
import dev.cel.runtime.CelFunctionBinding;
5657
import dev.cel.runtime.CelRuntime;
5758
import dev.cel.runtime.CelRuntimeFactory;
@@ -579,6 +580,23 @@ public void verifyOptimizedAstCorrectness_indexIsNotForwardReferencing_throws(St
579580
.contains("Illegal block index found. The index value must be less than");
580581
}
581582

583+
@Test
584+
public void block_containsCycle_throws() throws Exception {
585+
CelAbstractSyntaxTree ast = compileUsingInternalFunctions("cel.block([index1,index0],index0)");
586+
587+
CelEvaluationException e = assertThrows(CelEvaluationException.class, () -> CEL.createProgram(ast).eval());
588+
assertThat(e).hasMessageThat().contains("Cycle detected: @index0");
589+
}
590+
591+
@Test
592+
public void block_withNonStrictFunctions_noCycle_success() throws Exception {
593+
CelAbstractSyntaxTree ast = compileUsingInternalFunctions("cel.block([1/0 > 0, (index0 && false) && (index0 && true)],index1)");
594+
595+
boolean result = (boolean) CEL.createProgram(ast).eval();
596+
597+
assertThat(result).isFalse();
598+
}
599+
582600
/**
583601
* Converts AST containing cel.block related test functions to internal functions (e.g: cel.block
584602
* -> cel.@block)

runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.common.collect.ImmutableSet;
2626
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2727
import com.google.errorprone.annotations.Immutable;
28+
import java.util.Set;
2829
import javax.annotation.concurrent.ThreadSafe;
2930
import com.google.protobuf.ByteString;
3031
import com.google.protobuf.NullValue;
@@ -343,7 +344,13 @@ private IntermediateResult resolveIdent(ExecutionFrame frame, CelExpr expr, Stri
343344
Object value = rawResult.value();
344345
boolean isLazyExpression = value instanceof LazyExpression;
345346
if (isLazyExpression) {
346-
value = evalInternal(frame, ((LazyExpression) value).celExpr).value();
347+
frame.markLazyEvaluationOrThrow(name);
348+
349+
try {
350+
value = evalInternal(frame, ((LazyExpression) value).celExpr).value();
351+
} finally {
352+
frame.endLazyEvaluation(name);
353+
}
347354
}
348355

349356
// Value resolved from Binding, it could be Message, PartialMessage or unbound(null)
@@ -1065,7 +1072,11 @@ private IntermediateResult evalCelBlock(
10651072
}
10661073
frame.pushLazyScope(Collections.unmodifiableMap(blockList));
10671074

1068-
return evalInternal(frame, blockCall.args().get(1));
1075+
try {
1076+
return evalInternal(frame, blockCall.args().get(1));
1077+
} finally {
1078+
frame.popScope();
1079+
}
10691080
}
10701081

10711082
private CelType getCheckedTypeOrThrow(CelExpr expr) throws CelEvaluationException {
@@ -1115,6 +1126,7 @@ static class ExecutionFrame {
11151126
private final int maxIterations;
11161127
private final ArrayDeque<RuntimeUnknownResolver> resolvers;
11171128
private final Optional<? extends CelFunctionResolver> lateBoundFunctionResolver;
1129+
private final Set<String> activeLazyAttributes = new HashSet<>();
11181130
private RuntimeUnknownResolver currentResolver;
11191131
private int iterations;
11201132
@VisibleForTesting int scopeLevel;
@@ -1132,6 +1144,17 @@ private ExecutionFrame(
11321144
this.maxIterations = maxIterations;
11331145
}
11341146

1147+
private void markLazyEvaluationOrThrow(String name) {
1148+
boolean added = activeLazyAttributes.add(name);
1149+
if (!added) {
1150+
throw new IllegalStateException(String.format("Cycle detected: %s", name));
1151+
}
1152+
}
1153+
1154+
private void endLazyEvaluation(String name) {
1155+
activeLazyAttributes.remove(name);
1156+
}
1157+
11351158
private Optional<CelEvaluationListener> getEvaluationListener() {
11361159
return evaluationListener;
11371160
}

0 commit comments

Comments
 (0)