From 08ab6d62a5d55ca9bf95ce1e96b0600bbc021304 Mon Sep 17 00:00:00 2001 From: CEL Dev Team Date: Tue, 23 Jun 2026 04:38:38 -0700 Subject: [PATCH] Internal Change PiperOrigin-RevId: 936580427 --- .../dev/cel/extensions/CelExtensions.java | 9 ++++ .../dev/cel/extensions/CelExtensionsTest.java | 6 +++ .../optimizers/ConstantFoldingOptimizer.java | 43 +++++++++++-------- .../ConstantFoldingOptimizerTest.java | 5 +++ 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/extensions/src/main/java/dev/cel/extensions/CelExtensions.java b/extensions/src/main/java/dev/cel/extensions/CelExtensions.java index 8adc39384..446fa26e7 100644 --- a/extensions/src/main/java/dev/cel/extensions/CelExtensions.java +++ b/extensions/src/main/java/dev/cel/extensions/CelExtensions.java @@ -23,6 +23,7 @@ import dev.cel.extensions.CelMathExtensions.Function; import java.util.EnumSet; import java.util.Set; +import java.util.stream.Stream; /** * Collections of CEL Extensions. @@ -381,6 +382,14 @@ public static ImmutableSet getAllFunctionNames() { .map(CelListsExtensions.Function::getFunction), EnumSet.allOf(CelRegexExtensions.Function.class).stream() .map(CelRegexExtensions.Function::getFunction), + Stream.of( + CelOptionalLibrary.Function.VALUE, + CelOptionalLibrary.Function.HAS_VALUE, + CelOptionalLibrary.Function.OPTIONAL_NONE, + CelOptionalLibrary.Function.OPTIONAL_OF, + CelOptionalLibrary.Function.OPTIONAL_UNWRAP, + CelOptionalLibrary.Function.OPTIONAL_OF_NON_ZERO_VALUE) + .map(CelOptionalLibrary.Function::getFunction), EnumSet.allOf(CelComprehensionsExtensions.Function.class).stream() .map(CelComprehensionsExtensions.Function::getFunction)) .collect(toImmutableSet()); diff --git a/extensions/src/test/java/dev/cel/extensions/CelExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelExtensionsTest.java index 192630ea3..31c7d65c8 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelExtensionsTest.java @@ -189,6 +189,12 @@ public void getAllFunctionNames() { "regex.replace", "regex.extract", "regex.extractAll", + "value", + "hasValue", + "optional.none", + "optional.of", + "optional.unwrap", + "optional.ofNonZeroValue", "cel.@mapInsert"); } } diff --git a/optimizer/src/main/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizer.java b/optimizer/src/main/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizer.java index b3c0e3046..46f801bb8 100644 --- a/optimizer/src/main/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizer.java +++ b/optimizer/src/main/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizer.java @@ -369,30 +369,37 @@ private Optional maybeAdaptEvaluatedResult(Object result) { private Optional maybeRewriteOptional( Optional optResult, CelMutableAst mutableAst, CelMutableExpr expr) { - if (!optResult.isPresent()) { - if (!expr.call().function().equals(Function.OPTIONAL_NONE.getFunction())) { - // An empty optional value was encountered. Rewrite the tree with optional.none call. - // This is to account for other optional functions returning an empty optional value - // e.g: optional.ofNonZeroValue(0) - return Optional.of(astMutator.replaceSubtree(mutableAst, newOptionalNoneExpr(), expr.id())); - } - } else if (!expr.call().function().equals(Function.OPTIONAL_OF.getFunction())) { - Object unwrappedResult = optResult.get(); - if (!CelConstant.isConstantValue(unwrappedResult)) { - // Evaluated result is not a constant. Leave the optional as is. + Object unwrappedResult = optResult.orElse(null); + if (unwrappedResult == null) { + if (isCallToFunction(expr, Function.OPTIONAL_NONE.getFunction())) { return Optional.empty(); } + // An empty optional value was encountered. Rewrite the tree with optional.none call. + // This is to account for other optional functions returning an empty optional value + // e.g: optional.ofNonZeroValue(0) + return Optional.of(astMutator.replaceSubtree(mutableAst, newOptionalNoneExpr(), expr.id())); + } - CelMutableExpr newOptionalOfCall = - CelMutableExpr.ofCall( - CelMutableCall.create( - Function.OPTIONAL_OF.getFunction(), - CelMutableExpr.ofConstant(CelConstant.ofObjectValue(unwrappedResult)))); + if (isCallToFunction(expr, Function.OPTIONAL_OF.getFunction())) { + return Optional.empty(); + } - return Optional.of(astMutator.replaceSubtree(mutableAst, newOptionalOfCall, expr.id())); + if (!CelConstant.isConstantValue(unwrappedResult)) { + // Evaluated result is not a constant. Leave the optional as is. + return Optional.empty(); } - return Optional.empty(); + CelMutableExpr newOptionalOfCall = + CelMutableExpr.ofCall( + CelMutableCall.create( + Function.OPTIONAL_OF.getFunction(), + CelMutableExpr.ofConstant(CelConstant.ofObjectValue(unwrappedResult)))); + + return Optional.of(astMutator.replaceSubtree(mutableAst, newOptionalOfCall, expr.id())); + } + + private static boolean isCallToFunction(CelMutableExpr expr, String functionName) { + return expr.getKind().equals(Kind.CALL) && expr.call().function().equals(functionName); } /** Inspects the non-strict calls to determine whether a branch can be removed. */ diff --git a/optimizer/src/test/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizerTest.java b/optimizer/src/test/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizerTest.java index ab5508064..66f5a94d7 100644 --- a/optimizer/src/test/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizerTest.java +++ b/optimizer/src/test/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizerTest.java @@ -302,6 +302,9 @@ public void constantFold_success(String source, String expected) throws Exceptio "{source: 'cel.bind(r0, [1, 2, 3], cel.bind(r1, 1 in r0 && 2 in x, r1))', expected:" + " 'cel.bind(r0, [1, 2, 3], cel.bind(r1, 1 in r0 && 2 in x, r1))'}") @TestParameters("{source: 'false ? false : cel.bind(a, x, a)', expected: 'cel.bind(a, x, a)'}") + @TestParameters( + "{source: 'cel.bind(myMap, {\"foo\": \"bar\"}, myMap[?\"foo\"].optMap(x, x + \"baz\"))', " + + "expected: 'optional.of(\"barbaz\")'}") public void constantFold_macros_macroCallMetadataPopulated(String source, String expected) throws Exception { Cel cel = @@ -557,4 +560,6 @@ public void iterationLimitReached_throws() throws Exception { assertThrows(CelOptimizationException.class, () -> optimizer.optimize(ast)); assertThat(e).hasMessageThat().contains("Optimization failure: Max iteration count reached."); } + + }