diff --git a/bundle/src/main/java/dev/cel/bundle/CelBuilder.java b/bundle/src/main/java/dev/cel/bundle/CelBuilder.java index 1dadaeb39..f603b479f 100644 --- a/bundle/src/main/java/dev/cel/bundle/CelBuilder.java +++ b/bundle/src/main/java/dev/cel/bundle/CelBuilder.java @@ -165,6 +165,14 @@ public interface CelBuilder { @CanIgnoreReturnValue CelBuilder addFunctionBindings(Iterable bindings); + /** Adds bindings for functions that are allowed to be late-bound (resolved at execution time). */ + @CanIgnoreReturnValue + CelBuilder addLateBoundFunctions(String... lateBoundFunctionNames); + + /** Adds bindings for functions that are allowed to be late-bound (resolved at execution time). */ + @CanIgnoreReturnValue + CelBuilder addLateBoundFunctions(Iterable lateBoundFunctionNames); + /** Set the expected {@code resultType} for the type-checked expression. */ @CanIgnoreReturnValue CelBuilder setResultType(CelType resultType); diff --git a/bundle/src/main/java/dev/cel/bundle/CelImpl.java b/bundle/src/main/java/dev/cel/bundle/CelImpl.java index ae0ab2395..f6b985065 100644 --- a/bundle/src/main/java/dev/cel/bundle/CelImpl.java +++ b/bundle/src/main/java/dev/cel/bundle/CelImpl.java @@ -281,6 +281,18 @@ public CelBuilder addFunctionBindings(Iterable lateBoundFunctionNames) { + runtimeBuilder.addLateBoundFunctions(lateBoundFunctionNames); + return this; + } + @Override public CelBuilder setResultType(CelType resultType) { checkNotNull(resultType); diff --git a/common/src/main/java/dev/cel/common/values/CelValueConverter.java b/common/src/main/java/dev/cel/common/values/CelValueConverter.java index 2af0a76cb..70d04acc8 100644 --- a/common/src/main/java/dev/cel/common/values/CelValueConverter.java +++ b/common/src/main/java/dev/cel/common/values/CelValueConverter.java @@ -117,7 +117,7 @@ protected Object normalizePrimitive(Object value) { } /** Adapts a {@link CelValue} to a plain old Java Object. */ - private static Object unwrap(CelValue celValue) { + private Object unwrap(CelValue celValue) { Preconditions.checkNotNull(celValue); if (celValue instanceof OptionalValue) { @@ -126,7 +126,7 @@ private static Object unwrap(CelValue celValue) { return Optional.empty(); } - return Optional.of(optionalValue.value()); + return Optional.of(maybeUnwrap(optionalValue.value())); } if (celValue instanceof ErrorValue) { diff --git a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel index 2eb26846f..454b2a2fd 100644 --- a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel +++ b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel @@ -42,6 +42,7 @@ java_library( ":strings", "//common:options", "//extensions:extension_library", + "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", ], ) diff --git a/extensions/src/main/java/dev/cel/extensions/CelExtensions.java b/extensions/src/main/java/dev/cel/extensions/CelExtensions.java index 2d14ed118..8f1770f3f 100644 --- a/extensions/src/main/java/dev/cel/extensions/CelExtensions.java +++ b/extensions/src/main/java/dev/cel/extensions/CelExtensions.java @@ -19,7 +19,9 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Streams; +import com.google.errorprone.annotations.InlineMe; import dev.cel.common.CelOptions; +import dev.cel.extensions.CelMathExtensions.Function; import java.util.Set; /** @@ -121,12 +123,9 @@ public static CelProtoExtensions protos() { *

This will include all functions denoted in {@link CelMathExtensions.Function}, including any * future additions. To expose only a subset of these, use {@link #math(CelOptions, * CelMathExtensions.Function...)} or {@link #math(CelOptions,int)} instead. - * - * @param celOptions CelOptions to configure CelMathExtension with. This should be the same - * options object used to configure the compilation/runtime environments. */ - public static CelMathExtensions math(CelOptions celOptions) { - return CelMathExtensions.library(celOptions).latest(); + public static CelMathExtensions math() { + return CelMathExtensions.library().latest(); } /** @@ -134,8 +133,8 @@ public static CelMathExtensions math(CelOptions celOptions) { * *

Refer to README.md for functions available in each version. */ - public static CelMathExtensions math(CelOptions celOptions, int version) { - return CelMathExtensions.library(celOptions).version(version); + public static CelMathExtensions math(int version) { + return CelMathExtensions.library().version(version); } /** @@ -150,13 +149,9 @@ public static CelMathExtensions math(CelOptions celOptions, int version) { * collision. * *

This will include only the specific functions denoted by {@link CelMathExtensions.Function}. - * - * @param celOptions CelOptions to configure CelMathExtension with. This should be the same - * options object used to configure the compilation/runtime environments. */ - public static CelMathExtensions math( - CelOptions celOptions, CelMathExtensions.Function... functions) { - return math(celOptions, ImmutableSet.copyOf(functions)); + public static CelMathExtensions math(CelMathExtensions.Function... functions) { + return math(ImmutableSet.copyOf(functions)); } /** @@ -171,13 +166,49 @@ public static CelMathExtensions math( * collision. * *

This will include only the specific functions denoted by {@link CelMathExtensions.Function}. - * - * @param celOptions CelOptions to configure CelMathExtension with. This should be the same - * options object used to configure the compilation/runtime environments. */ + public static CelMathExtensions math(Set functions) { + return new CelMathExtensions(functions); + } + + /** + * @deprecated Use {@link #math()} instead. + */ + @Deprecated + @InlineMe(replacement = "CelExtensions.math()", imports = "dev.cel.extensions.CelExtensions") + public static CelMathExtensions math(CelOptions unused) { + return math(); + } + + /** + * @deprecated Use {@link #math(int)} instead. + */ + @Deprecated + @InlineMe( + replacement = "CelExtensions.math(version)", + imports = "dev.cel.extensions.CelExtensions") + public static CelMathExtensions math(CelOptions unused, int version) { + return math(version); + } + + /** + * @deprecated Use {@link #math(Function...)} instead. + */ + @Deprecated + public static CelMathExtensions math(CelOptions unused, CelMathExtensions.Function... functions) { + return math(ImmutableSet.copyOf(functions)); + } + + /** + * @deprecated Use {@link #math(Set)} instead. + */ + @Deprecated + @InlineMe( + replacement = "CelExtensions.math(functions)", + imports = "dev.cel.extensions.CelExtensions") public static CelMathExtensions math( - CelOptions celOptions, Set functions) { - return new CelMathExtensions(celOptions, functions); + CelOptions unused, Set functions) { + return math(functions); } /** @@ -354,7 +385,7 @@ public static CelExtensionLibrary getE case "lists": return CelListsExtensions.library(); case "math": - return CelMathExtensions.library(options); + return CelMathExtensions.library(); case "optional": return CelOptionalLibrary.library(); case "protos": diff --git a/extensions/src/main/java/dev/cel/extensions/CelMathExtensions.java b/extensions/src/main/java/dev/cel/extensions/CelMathExtensions.java index 22336eb22..78a0fd51c 100644 --- a/extensions/src/main/java/dev/cel/extensions/CelMathExtensions.java +++ b/extensions/src/main/java/dev/cel/extensions/CelMathExtensions.java @@ -27,7 +27,6 @@ import dev.cel.checker.CelCheckerBuilder; import dev.cel.common.CelFunctionDecl; import dev.cel.common.CelIssue; -import dev.cel.common.CelOptions; import dev.cel.common.CelOverloadDecl; import dev.cel.common.ast.CelConstant; import dev.cel.common.ast.CelExpr; @@ -136,7 +135,8 @@ public final class CelMathExtensions return builder.buildOrThrow(); } - enum Function { + /** Enumeration of functions for Math extension. */ + public enum Function { MAX( CelFunctionDecl.newFunctionDeclaration( MATH_MAX_FUNCTION, @@ -206,51 +206,59 @@ enum Function { MATH_MAX_OVERLOAD_DOC, SimpleType.DYN, ListType.create(SimpleType.DYN))), - ImmutableSet.of( - CelFunctionBinding.from("math_@max_double", Double.class, x -> x), - CelFunctionBinding.from("math_@max_int", Long.class, x -> x), - CelFunctionBinding.from( - "math_@max_double_double", Double.class, Double.class, CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_int_int", Long.class, Long.class, CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_int_double", Long.class, Double.class, CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_double_int", Double.class, Long.class, CelMathExtensions::maxPair), - CelFunctionBinding.from("math_@max_list_dyn", List.class, CelMathExtensions::maxList)), - ImmutableSet.of( - CelFunctionBinding.from("math_@max_uint", Long.class, x -> x), - CelFunctionBinding.from( - "math_@max_uint_uint", Long.class, Long.class, CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_double_uint", Double.class, Long.class, CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_uint_int", Long.class, Long.class, CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_uint_double", Long.class, Double.class, CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_int_uint", Long.class, Long.class, CelMathExtensions::maxPair)), - ImmutableSet.of( - CelFunctionBinding.from("math_@max_uint", UnsignedLong.class, x -> x), - CelFunctionBinding.from( - "math_@max_uint_uint", - UnsignedLong.class, - UnsignedLong.class, - CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_double_uint", - Double.class, - UnsignedLong.class, - CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_uint_int", UnsignedLong.class, Long.class, CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_uint_double", - UnsignedLong.class, - Double.class, - CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_int_uint", Long.class, UnsignedLong.class, CelMathExtensions::maxPair))), + ImmutableSet.builder() + .add(CelFunctionBinding.from("math_@max_double", Double.class, x -> x)) + .add(CelFunctionBinding.from("math_@max_int", Long.class, x -> x)) + .add( + CelFunctionBinding.from( + "math_@max_double_double", + Double.class, + Double.class, + CelMathExtensions::maxPair)) + .add( + CelFunctionBinding.from( + "math_@max_int_int", Long.class, Long.class, CelMathExtensions::maxPair)) + .add( + CelFunctionBinding.from( + "math_@max_int_double", Long.class, Double.class, CelMathExtensions::maxPair)) + .add( + CelFunctionBinding.from( + "math_@max_double_int", Double.class, Long.class, CelMathExtensions::maxPair)) + .add( + CelFunctionBinding.from( + "math_@max_list_dyn", List.class, CelMathExtensions::maxList)) + .add(CelFunctionBinding.from("math_@max_uint", UnsignedLong.class, x -> x)) + .add( + CelFunctionBinding.from( + "math_@max_uint_uint", + UnsignedLong.class, + UnsignedLong.class, + CelMathExtensions::maxPair)) + .add( + CelFunctionBinding.from( + "math_@max_double_uint", + Double.class, + UnsignedLong.class, + CelMathExtensions::maxPair)) + .add( + CelFunctionBinding.from( + "math_@max_uint_int", + UnsignedLong.class, + Long.class, + CelMathExtensions::maxPair)) + .add( + CelFunctionBinding.from( + "math_@max_uint_double", + UnsignedLong.class, + Double.class, + CelMathExtensions::maxPair)) + .add( + CelFunctionBinding.from( + "math_@max_int_uint", + Long.class, + UnsignedLong.class, + CelMathExtensions::maxPair)) + .build()), MIN( CelFunctionDecl.newFunctionDeclaration( MATH_MIN_FUNCTION, @@ -320,51 +328,59 @@ enum Function { MATH_MIN_OVERLOAD_DOC, SimpleType.DYN, ListType.create(SimpleType.DYN))), - ImmutableSet.of( - CelFunctionBinding.from("math_@min_double", Double.class, x -> x), - CelFunctionBinding.from("math_@min_int", Long.class, x -> x), - CelFunctionBinding.from( - "math_@min_double_double", Double.class, Double.class, CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_int_int", Long.class, Long.class, CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_int_double", Long.class, Double.class, CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_double_int", Double.class, Long.class, CelMathExtensions::minPair), - CelFunctionBinding.from("math_@min_list_dyn", List.class, CelMathExtensions::minList)), - ImmutableSet.of( - CelFunctionBinding.from("math_@min_uint", Long.class, x -> x), - CelFunctionBinding.from( - "math_@min_uint_uint", Long.class, Long.class, CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_double_uint", Double.class, Long.class, CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_uint_int", Long.class, Long.class, CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_uint_double", Long.class, Double.class, CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_int_uint", Long.class, Long.class, CelMathExtensions::minPair)), - ImmutableSet.of( - CelFunctionBinding.from("math_@min_uint", UnsignedLong.class, x -> x), - CelFunctionBinding.from( - "math_@min_uint_uint", - UnsignedLong.class, - UnsignedLong.class, - CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_double_uint", - Double.class, - UnsignedLong.class, - CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_uint_int", UnsignedLong.class, Long.class, CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_uint_double", - UnsignedLong.class, - Double.class, - CelMathExtensions::minPair), - CelFunctionBinding.from( - "math_@min_int_uint", Long.class, UnsignedLong.class, CelMathExtensions::minPair))), + ImmutableSet.builder() + .add(CelFunctionBinding.from("math_@min_double", Double.class, x -> x)) + .add(CelFunctionBinding.from("math_@min_int", Long.class, x -> x)) + .add( + CelFunctionBinding.from( + "math_@min_double_double", + Double.class, + Double.class, + CelMathExtensions::minPair)) + .add( + CelFunctionBinding.from( + "math_@min_int_int", Long.class, Long.class, CelMathExtensions::minPair)) + .add( + CelFunctionBinding.from( + "math_@min_int_double", Long.class, Double.class, CelMathExtensions::minPair)) + .add( + CelFunctionBinding.from( + "math_@min_double_int", Double.class, Long.class, CelMathExtensions::minPair)) + .add( + CelFunctionBinding.from( + "math_@min_list_dyn", List.class, CelMathExtensions::minList)) + .add(CelFunctionBinding.from("math_@min_uint", UnsignedLong.class, x -> x)) + .add( + CelFunctionBinding.from( + "math_@min_uint_uint", + UnsignedLong.class, + UnsignedLong.class, + CelMathExtensions::minPair)) + .add( + CelFunctionBinding.from( + "math_@min_double_uint", + Double.class, + UnsignedLong.class, + CelMathExtensions::minPair)) + .add( + CelFunctionBinding.from( + "math_@min_uint_int", + UnsignedLong.class, + Long.class, + CelMathExtensions::minPair)) + .add( + CelFunctionBinding.from( + "math_@min_uint_double", + UnsignedLong.class, + Double.class, + CelMathExtensions::minPair)) + .add( + CelFunctionBinding.from( + "math_@min_int_uint", + Long.class, + UnsignedLong.class, + CelMathExtensions::minPair)) + .build()), CEIL( CelFunctionDecl.newFunctionDeclaration( MATH_CEIL_FUNCTION, @@ -646,36 +662,14 @@ enum Function { private final CelFunctionDecl functionDecl; private final ImmutableSet functionBindings; - private final ImmutableSet functionBindingsULongSigned; - private final ImmutableSet functionBindingsULongUnsigned; String getFunction() { return functionDecl.name(); } Function(CelFunctionDecl functionDecl, ImmutableSet bindings) { - this(functionDecl, bindings, ImmutableSet.of(), ImmutableSet.of()); - } - - Function( - CelFunctionDecl functionDecl, - ImmutableSet functionBindings, - ImmutableSet functionBindingsULongSigned, - ImmutableSet functionBindingsULongUnsigned) { this.functionDecl = functionDecl; - this.functionBindings = - functionBindings.isEmpty() - ? ImmutableSet.of() - : CelFunctionBinding.fromOverloads(functionDecl.name(), functionBindings); - this.functionBindingsULongSigned = - functionBindingsULongSigned.isEmpty() - ? ImmutableSet.of() - : CelFunctionBinding.fromOverloads(functionDecl.name(), functionBindingsULongSigned); - this.functionBindingsULongUnsigned = - functionBindingsULongUnsigned.isEmpty() - ? ImmutableSet.of() - : CelFunctionBinding.fromOverloads( - functionDecl.name(), functionBindingsULongUnsigned); + this.functionBindings = bindings; } } @@ -684,10 +678,8 @@ private static final class Library implements CelExtensionLibrarybuilder() .addAll(version1.functions) .add(Function.SQRT) - .build(), - enableUnsignedLongs); + .build()); } @Override @@ -734,25 +724,20 @@ public ImmutableSet versions() { } } - private static final Library LIBRARY_UNSIGNED_LONGS_ENABLED = new Library(true); - private static final Library LIBRARY_UNSIGNED_LONGS_DISABLED = new Library(false); + private static final Library LIBRARY = new Library(); - static CelExtensionLibrary library(CelOptions celOptions) { - return celOptions.enableUnsignedLongs() - ? LIBRARY_UNSIGNED_LONGS_ENABLED - : LIBRARY_UNSIGNED_LONGS_DISABLED; + static CelExtensionLibrary library() { + return LIBRARY; } - private final boolean enableUnsignedLongs; private final ImmutableSet functions; private final int version; - CelMathExtensions(CelOptions celOptions, Set functions) { - this(-1, functions, celOptions.enableUnsignedLongs()); + CelMathExtensions(Set functions) { + this(-1, functions); } - private CelMathExtensions(int version, Set functions, boolean enableUnsignedLongs) { - this.enableUnsignedLongs = enableUnsignedLongs; + private CelMathExtensions(int version, Set functions) { this.version = version; this.functions = ImmutableSet.copyOf(functions); } @@ -788,11 +773,11 @@ public void setCheckerOptions(CelCheckerBuilder checkerBuilder) { public void setRuntimeOptions(CelRuntimeBuilder runtimeBuilder) { functions.forEach( function -> { - runtimeBuilder.addFunctionBindings(function.functionBindings); - runtimeBuilder.addFunctionBindings( - enableUnsignedLongs - ? function.functionBindingsULongUnsigned - : function.functionBindingsULongSigned); + ImmutableSet combined = function.functionBindings; + if (!combined.isEmpty()) { + runtimeBuilder.addFunctionBindings( + CelFunctionBinding.fromOverloads(function.functionDecl.name(), combined)); + } }); } diff --git a/extensions/src/test/java/dev/cel/extensions/BUILD.bazel b/extensions/src/test/java/dev/cel/extensions/BUILD.bazel index 19fd3657e..eed240317 100644 --- a/extensions/src/test/java/dev/cel/extensions/BUILD.bazel +++ b/extensions/src/test/java/dev/cel/extensions/BUILD.bazel @@ -12,6 +12,7 @@ java_library( "//bundle:cel", "//bundle:cel_experimental_factory", "//common:cel_ast", + "//common:cel_exception", "//common:compiler_common", "//common:container", "//common:options", diff --git a/extensions/src/test/java/dev/cel/extensions/CelBindingsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelBindingsExtensionsTest.java index b87967d0e..00fcad473 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelBindingsExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelBindingsExtensionsTest.java @@ -23,7 +23,6 @@ import com.google.testing.junit.testparameterinjector.TestParameterInjector; import com.google.testing.junit.testparameterinjector.TestParameters; import dev.cel.bundle.Cel; -import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelFunctionDecl; import dev.cel.common.CelOptions; import dev.cel.common.CelOverloadDecl; @@ -36,36 +35,24 @@ import dev.cel.parser.CelStandardMacro; import dev.cel.runtime.CelEvaluationException; import dev.cel.runtime.CelFunctionBinding; -import dev.cel.testing.CelRuntimeFlavor; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TestParameterInjector.class) -public final class CelBindingsExtensionsTest { - - @TestParameter public CelRuntimeFlavor runtimeFlavor; - @TestParameter public boolean isParseOnly; - - private Cel cel; - - @Before - public void setUp() { - // Legacy runtime does not support parsed-only evaluation mode. - Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); - cel = - runtimeFlavor - .builder() - .setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(true).build()) - .setStandardMacros(CelStandardMacro.STANDARD_MACROS) - .addCompilerLibraries(CelOptionalLibrary.INSTANCE, CelExtensions.bindings()) - .addRuntimeLibraries(CelOptionalLibrary.INSTANCE) - .build(); +public final class CelBindingsExtensionsTest extends CelExtensionTestBase { + + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(true).build()) + .setStandardMacros(CelStandardMacro.STANDARD_MACROS) + .addCompilerLibraries(CelOptionalLibrary.INSTANCE, CelExtensions.bindings()) + .addRuntimeLibraries(CelOptionalLibrary.INSTANCE) + .build(); } @Test @@ -331,21 +318,5 @@ public void lazyBinding_boundAttributeInNestedComprehension() throws Exception { assertThat(invocation.get()).isEqualTo(1); } - private Object eval(Cel cel, String expression) throws Exception { - return eval(cel, expression, ImmutableMap.of()); - } - - private Object eval(Cel cel, String expression, Map variables) throws Exception { - CelAbstractSyntaxTree ast; - if (isParseOnly) { - ast = cel.parse(expression).getAst(); - } else { - ast = cel.compile(expression).getAst(); - } - return cel.createProgram(ast).eval(variables); - } - private Object eval(String expression) throws Exception { - return eval(this.cel, expression, ImmutableMap.of()); - } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java index 207178cfe..42dc3e07d 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java @@ -19,7 +19,6 @@ import static org.junit.Assert.assertThrows; import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableMap; import com.google.testing.junit.testparameterinjector.TestParameter; import com.google.testing.junit.testparameterinjector.TestParameterInjector; import com.google.testing.junit.testparameterinjector.TestParameters; @@ -40,15 +39,13 @@ import dev.cel.parser.CelUnparserFactory; import dev.cel.runtime.CelEvaluationException; import dev.cel.testing.CelRuntimeFlavor; -import java.util.Map; import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; /** Test for {@link CelExtensions#comprehensions()} */ @RunWith(TestParameterInjector.class) -public class CelComprehensionsExtensionsTest { +public class CelComprehensionsExtensionsTest extends CelExtensionTestBase { private static final CelOptions CEL_OPTIONS = CelOptions.current() @@ -57,29 +54,21 @@ public class CelComprehensionsExtensionsTest { .populateMacroCalls(true) .build(); - @TestParameter public CelRuntimeFlavor runtimeFlavor; - @TestParameter public boolean isParseOnly; - - private Cel cel; - - @Before - public void setUp() { - // Legacy runtime does not support parsed-only evaluation mode. - Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); - this.cel = - runtimeFlavor - .builder() - .setOptions(CEL_OPTIONS) - .setStandardMacros(CelStandardMacro.STANDARD_MACROS) - .addCompilerLibraries(CelExtensions.comprehensions()) - .addCompilerLibraries(CelExtensions.lists()) - .addCompilerLibraries(CelExtensions.strings()) - .addCompilerLibraries(CelOptionalLibrary.INSTANCE, CelExtensions.bindings()) - .addRuntimeLibraries(CelOptionalLibrary.INSTANCE) - .addRuntimeLibraries(CelExtensions.lists()) - .addRuntimeLibraries(CelExtensions.strings()) - .addRuntimeLibraries(CelExtensions.comprehensions()) - .build(); + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .setOptions(CEL_OPTIONS) + .setStandardMacros(CelStandardMacro.STANDARD_MACROS) + .addCompilerLibraries(CelExtensions.comprehensions()) + .addCompilerLibraries(CelExtensions.lists()) + .addCompilerLibraries(CelExtensions.strings()) + .addCompilerLibraries(CelOptionalLibrary.INSTANCE, CelExtensions.bindings()) + .addRuntimeLibraries(CelOptionalLibrary.INSTANCE) + .addRuntimeLibraries(CelExtensions.lists()) + .addRuntimeLibraries(CelExtensions.strings()) + .addRuntimeLibraries(CelExtensions.comprehensions()) + .build(); } private static final CelUnparser UNPARSER = CelUnparserFactory.newUnparser(); @@ -376,17 +365,5 @@ public void mutableMapValue_select_missingKeyException() throws Exception { assertThat(e).hasCauseThat().hasMessageThat().contains("key 'b' is not present in map."); } - private Object eval(String expression) throws Exception { - return eval(this.cel, expression, ImmutableMap.of()); - } - private Object eval(Cel cel, String expression, Map variables) throws Exception { - CelAbstractSyntaxTree ast; - if (isParseOnly) { - ast = cel.parse(expression).getAst(); - } else { - ast = cel.compile(expression).getAst(); - } - return cel.createProgram(ast).eval(variables); - } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java index b0a501ddb..afeaa9105 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelEncoderExtensionsTest.java @@ -19,44 +19,32 @@ import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableMap; -import com.google.testing.junit.testparameterinjector.TestParameter; import com.google.testing.junit.testparameterinjector.TestParameterInjector; import dev.cel.bundle.Cel; -import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelFunctionDecl; import dev.cel.common.CelOptions; import dev.cel.common.CelValidationException; import dev.cel.common.types.SimpleType; import dev.cel.common.values.CelByteString; import dev.cel.runtime.CelEvaluationException; -import dev.cel.testing.CelRuntimeFlavor; import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TestParameterInjector.class) -public class CelEncoderExtensionsTest { +public class CelEncoderExtensionsTest extends CelExtensionTestBase { private static final CelOptions CEL_OPTIONS = CelOptions.current().enableHeterogeneousNumericComparisons(true).build(); - @TestParameter public CelRuntimeFlavor runtimeFlavor; - @TestParameter public boolean isParseOnly; - - private Cel cel; - - @Before - public void setUp() { - // Legacy runtime does not support parsed-only evaluation mode. - Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); - this.cel = - runtimeFlavor - .builder() - .setOptions(CEL_OPTIONS) - .addCompilerLibraries(CelExtensions.encoders(CEL_OPTIONS)) - .addRuntimeLibraries(CelExtensions.encoders(CEL_OPTIONS)) - .addVar("stringVar", SimpleType.STRING) - .build(); + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .setOptions(CEL_OPTIONS) + .addCompilerLibraries(CelExtensions.encoders(CEL_OPTIONS)) + .addRuntimeLibraries(CelExtensions.encoders(CEL_OPTIONS)) + .addVar("stringVar", SimpleType.STRING) + .build(); } @Test @@ -132,12 +120,5 @@ public void decode_malformedBase64Char_throwsEvaluationException() throws Except assertThat(e).hasCauseThat().hasMessageThat().contains("Illegal base64 character"); } - private Object eval(String expr) throws Exception { - return eval(expr, ImmutableMap.of()); - } - private Object eval(String expr, ImmutableMap vars) throws Exception { - CelAbstractSyntaxTree ast = isParseOnly ? cel.parse(expr).getAst() : cel.compile(expr).getAst(); - return cel.createProgram(ast).eval(vars); - } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelExtensionTestBase.java b/extensions/src/test/java/dev/cel/extensions/CelExtensionTestBase.java new file mode 100644 index 000000000..c80ee38b6 --- /dev/null +++ b/extensions/src/test/java/dev/cel/extensions/CelExtensionTestBase.java @@ -0,0 +1,66 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev.cel.extensions; + +import com.google.common.collect.ImmutableMap; +import com.google.testing.junit.testparameterinjector.TestParameter; +import dev.cel.bundle.Cel; +import dev.cel.common.CelAbstractSyntaxTree; +import dev.cel.common.CelException; +import dev.cel.testing.CelRuntimeFlavor; +import java.util.Map; +import org.junit.Assume; +import org.junit.Before; + +/** + * Abstract base class for extension tests to facilitate executing tests with both legacy and + * planner runtime, along with parsed-only and checked expression evaluations for the planner. + */ +abstract class CelExtensionTestBase { + @TestParameter public CelRuntimeFlavor runtimeFlavor; + @TestParameter public boolean isParseOnly; + + @Before + public void setUpBase() { + // Legacy runtime does not support parsed-only evaluation. + Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); + this.cel = newCelEnv(); + } + + protected Cel cel; + + /** + * Subclasses must implement this to provide a Cel instance configured with the specific + * extensions being tested. + */ + protected abstract Cel newCelEnv(); + + protected Object eval(String expr) throws CelException { + return eval(cel, expr, ImmutableMap.of()); + } + + protected Object eval(String expr, Map variables) throws CelException { + return eval(cel, expr, variables); + } + + protected Object eval(Cel cel, String expr) throws CelException { + return eval(cel, expr, ImmutableMap.of()); + } + + protected Object eval(Cel cel, String expr, Map variables) throws CelException { + CelAbstractSyntaxTree ast = isParseOnly ? cel.parse(expr).getAst() : cel.compile(expr).getAst(); + return cel.createProgram(ast).eval(variables); + } +} diff --git a/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java index f36d90e2d..4520f81ba 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelListsExtensionsTest.java @@ -19,12 +19,9 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedMultiset; import com.google.common.collect.ImmutableSortedSet; -import com.google.testing.junit.testparameterinjector.TestParameter; import com.google.testing.junit.testparameterinjector.TestParameterInjector; import com.google.testing.junit.testparameterinjector.TestParameters; import dev.cel.bundle.Cel; -import dev.cel.bundle.CelBuilder; -import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelContainer; import dev.cel.common.CelValidationException; import dev.cel.common.CelValidationResult; @@ -33,24 +30,24 @@ import dev.cel.parser.CelStandardMacro; import dev.cel.runtime.CelEvaluationException; import dev.cel.testing.CelRuntimeFlavor; -import java.util.Map; import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TestParameterInjector.class) -public class CelListsExtensionsTest { - @TestParameter public CelRuntimeFlavor runtimeFlavor; - @TestParameter public boolean isParseOnly; +public class CelListsExtensionsTest extends CelExtensionTestBase { - private Cel cel; - - @Before - public void setUp() { - // Legacy runtime does not support parsed-only evaluation mode. - Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); - this.cel = setupEnv(runtimeFlavor.builder()); + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .setStandardMacros(CelStandardMacro.STANDARD_MACROS) + .addCompilerLibraries(CelExtensions.lists()) + .addRuntimeLibraries(CelExtensions.lists()) + .setContainer(CelContainer.ofName("cel.expr.conformance.test")) + .addMessageTypes(SimpleTest.getDescriptor()) + .addVar("non_list", SimpleType.DYN) + .build(); } @Test @@ -147,7 +144,7 @@ public void flatten_negativeDepth_throws() { CelEvaluationException e = assertThrows(CelEvaluationException.class, () -> eval(cel, "[1,2,3,4].flatten(-1)")); - if (isParseOnly) { + if (runtimeFlavor.equals(CelRuntimeFlavor.PLANNER)) { assertThat(e) .hasMessageThat() .contains("evaluation error at :17: Function 'flatten' failed"); @@ -322,23 +319,5 @@ public void sortBy_throws_evaluationException(String expression, String expected .contains(expectedError); } - private static Cel setupEnv(CelBuilder celBuilder) { - return celBuilder - .setStandardMacros(CelStandardMacro.STANDARD_MACROS) - .addCompilerLibraries(CelExtensions.lists()) - .addRuntimeLibraries(CelExtensions.lists()) - .setContainer(CelContainer.ofName("cel.expr.conformance.test")) - .addMessageTypes(SimpleTest.getDescriptor()) - .addVar("non_list", SimpleType.DYN) - .build(); - } - - private Object eval(Cel cel, String expr) throws Exception { - return eval(cel, expr, ImmutableMap.of()); - } - private Object eval(Cel cel, String expr, Map vars) throws Exception { - CelAbstractSyntaxTree ast = isParseOnly ? cel.parse(expr).getAst() : cel.compile(expr).getAst(); - return cel.createProgram(ast).eval(vars); - } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelMathExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelMathExtensionsTest.java index bcdfb0a21..383e50aa2 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelMathExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelMathExtensionsTest.java @@ -20,8 +20,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.primitives.UnsignedLong; +import com.google.testing.junit.testparameterinjector.TestParameter; import com.google.testing.junit.testparameterinjector.TestParameterInjector; import com.google.testing.junit.testparameterinjector.TestParameters; +import dev.cel.bundle.Cel; import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelFunctionDecl; import dev.cel.common.CelOptions; @@ -35,34 +37,36 @@ import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.CelRuntime; import dev.cel.runtime.CelRuntimeFactory; +import dev.cel.testing.CelRuntimeFlavor; +import java.util.Map; +import org.junit.Assume; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TestParameterInjector.class) public class CelMathExtensionsTest { - private static final CelOptions CEL_OPTIONS = - CelOptions.current().enableUnsignedLongs(false).build(); - private static final CelCompiler CEL_COMPILER = - CelCompilerFactory.standardCelCompilerBuilder() - .setOptions(CEL_OPTIONS) - .addLibraries(CelExtensions.math(CEL_OPTIONS)) - .build(); - private static final CelRuntime CEL_RUNTIME = - CelRuntimeFactory.standardCelRuntimeBuilder() - .setOptions(CEL_OPTIONS) - .addLibraries(CelExtensions.math(CEL_OPTIONS)) - .build(); - private static final CelOptions CEL_UNSIGNED_OPTIONS = CelOptions.current().build(); - private static final CelCompiler CEL_UNSIGNED_COMPILER = - CelCompilerFactory.standardCelCompilerBuilder() - .setOptions(CEL_UNSIGNED_OPTIONS) - .addLibraries(CelExtensions.math(CEL_UNSIGNED_OPTIONS)) - .build(); - private static final CelRuntime CEL_UNSIGNED_RUNTIME = - CelRuntimeFactory.standardCelRuntimeBuilder() - .setOptions(CEL_UNSIGNED_OPTIONS) - .addLibraries(CelExtensions.math(CEL_UNSIGNED_OPTIONS)) - .build(); + @TestParameter public CelRuntimeFlavor runtimeFlavor; + @TestParameter public boolean isParseOnly; + + private Cel cel; + + @Before + public void setUp() { + // Legacy runtime does not support parsed-only evaluation mode. + Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); + this.cel = + runtimeFlavor + .builder() + .setOptions( + CelOptions.current() + .enableHeterogeneousNumericComparisons( + runtimeFlavor.equals(CelRuntimeFlavor.PLANNER)) + .build()) + .addCompilerLibraries(CelExtensions.math()) + .addRuntimeLibraries(CelExtensions.math()) + .build(); + } @Test @TestParameters("{expr: 'math.greatest(-5)', expectedResult: -5}") @@ -97,9 +101,7 @@ public class CelMathExtensionsTest { "{expr: 'math.greatest([dyn(5.4), dyn(10), dyn(3u), dyn(-5.0), dyn(3.5)])', expectedResult:" + " 10}") public void greatest_intResult_success(String expr, long expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); - - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = eval(expr); assertThat(result).isEqualTo(expectedResult); } @@ -136,9 +138,7 @@ public void greatest_intResult_success(String expr, long expectedResult) throws "{expr: 'math.greatest([dyn(5.4), dyn(10.0), dyn(3u), dyn(-5.0), dyn(3.5)])', expectedResult:" + " 10.0}") public void greatest_doubleResult_success(String expr, double expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); - - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = eval(expr); assertThat(result).isEqualTo(expectedResult); } @@ -163,16 +163,16 @@ public void greatest_doubleResult_success(String expr, double expectedResult) th + " '10.0'}") public void greatest_doubleResult_withUnsignedLongsEnabled_success( String expr, double expectedResult) throws Exception { - CelOptions celOptions = CelOptions.current().enableUnsignedLongs(true).build(); + CelOptions celOptions = CelOptions.DEFAULT; CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder() .setOptions(celOptions) - .addLibraries(CelExtensions.math(celOptions)) + .addLibraries(CelExtensions.math()) .build(); CelRuntime celRuntime = CelRuntimeFactory.standardCelRuntimeBuilder() .setOptions(celOptions) - .addLibraries(CelExtensions.math(celOptions)) + .addLibraries(CelExtensions.math()) .build(); CelAbstractSyntaxTree ast = celCompiler.compile(expr).getAst(); @@ -182,44 +182,7 @@ public void greatest_doubleResult_withUnsignedLongsEnabled_success( } @Test - @TestParameters("{expr: 'math.greatest(5u)', expectedResult: 5}") - @TestParameters("{expr: 'math.greatest(1u, 1.0)', expectedResult: 1}") - @TestParameters("{expr: 'math.greatest(1u, 1)', expectedResult: 1}") - @TestParameters("{expr: 'math.greatest(1u, 1u)', expectedResult: 1}") - @TestParameters("{expr: 'math.greatest(3u, 3.0)', expectedResult: 3}") - @TestParameters("{expr: 'math.greatest(9u, 10u)', expectedResult: 10}") - @TestParameters("{expr: 'math.greatest(15u, 14u)', expectedResult: 15}") - @TestParameters( - "{expr: 'math.greatest(1, 9223372036854775807u)', expectedResult: 9223372036854775807}") - @TestParameters( - "{expr: 'math.greatest(9223372036854775807u, 1)', expectedResult: 9223372036854775807}") - @TestParameters("{expr: 'math.greatest(1u, 1, 1)', expectedResult: 1}") - @TestParameters("{expr: 'math.greatest(3u, 1u, 10u)', expectedResult: 10}") - @TestParameters("{expr: 'math.greatest(1u, 5u, 2u)', expectedResult: 5}") - @TestParameters("{expr: 'math.greatest(-1, 1u, 0u)', expectedResult: 1}") - @TestParameters("{expr: 'math.greatest(dyn(1u), 1, 1.0)', expectedResult: 1}") - @TestParameters("{expr: 'math.greatest(5u, 1.0, 3u)', expectedResult: 5}") - @TestParameters("{expr: 'math.greatest(5.4, 10u, 3u, -5.0, 3.5)', expectedResult: 10}") - @TestParameters( - "{expr: 'math.greatest(5.4, 10, 3u, -5.0, 9223372036854775807)', expectedResult:" - + " 9223372036854775807}") - @TestParameters( - "{expr: 'math.greatest(9223372036854775807, 10, 3u, -5.0, 0)', expectedResult:" - + " 9223372036854775807}") - @TestParameters("{expr: 'math.greatest([5.4, 10, 3u, -5.0, 3.5])', expectedResult: 10}") - @TestParameters( - "{expr: 'math.greatest([dyn(5.4), dyn(10), dyn(3u), dyn(-5.0), dyn(3.5)])', expectedResult:" - + " 10}") - public void greatest_unsignedLongResult_withSignedLongType_success( - String expr, long expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); - - Object result = CEL_RUNTIME.createProgram(ast).eval(); - - assertThat(result).isEqualTo(expectedResult); - } - - @Test + @TestParameters("{expr: 'math.greatest(5u)', expectedResult: '5'}") @TestParameters( "{expr: 'math.greatest(18446744073709551615u)', expectedResult: '18446744073709551615'}") @TestParameters("{expr: 'math.greatest(1u, 1.0)', expectedResult: '1'}") @@ -251,16 +214,16 @@ public void greatest_unsignedLongResult_withSignedLongType_success( + " '10'}") public void greatest_unsignedLongResult_withUnsignedLongType_success( String expr, String expectedResult) throws Exception { - CelOptions celOptions = CelOptions.current().enableUnsignedLongs(true).build(); + CelOptions celOptions = CelOptions.DEFAULT; CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder() .setOptions(celOptions) - .addLibraries(CelExtensions.math(celOptions)) + .addLibraries(CelExtensions.math()) .build(); CelRuntime celRuntime = CelRuntimeFactory.standardCelRuntimeBuilder() .setOptions(celOptions) - .addLibraries(CelExtensions.math(celOptions)) + .addLibraries(CelExtensions.math()) .build(); CelAbstractSyntaxTree ast = celCompiler.compile(expr).getAst(); @@ -271,9 +234,9 @@ public void greatest_unsignedLongResult_withUnsignedLongType_success( @Test public void greatest_noArgs_throwsCompilationException() { + Assume.assumeFalse(isParseOnly); CelValidationException e = - assertThrows( - CelValidationException.class, () -> CEL_COMPILER.compile("math.greatest()").getAst()); + assertThrows(CelValidationException.class, () -> cel.compile("math.greatest()").getAst()); assertThat(e).hasMessageThat().contains("math.greatest() requires at least one argument"); } @@ -283,8 +246,9 @@ public void greatest_noArgs_throwsCompilationException() { @TestParameters("{expr: 'math.greatest({})'}") @TestParameters("{expr: 'math.greatest([])'}") public void greatest_invalidSingleArg_throwsCompilationException(String expr) { + Assume.assumeFalse(isParseOnly); CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("math.greatest() invalid single argument value"); } @@ -297,8 +261,9 @@ public void greatest_invalidSingleArg_throwsCompilationException(String expr) { @TestParameters("{expr: 'math.greatest([1, {}, 2])'}") @TestParameters("{expr: 'math.greatest([1, [], 2])'}") public void greatest_invalidArgs_throwsCompilationException(String expr) { + Assume.assumeFalse(isParseOnly); CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e) .hasMessageThat() @@ -312,19 +277,16 @@ public void greatest_invalidArgs_throwsCompilationException(String expr) { @TestParameters("{expr: 'math.greatest([1, dyn({}), 2])'}") @TestParameters("{expr: 'math.greatest([1, dyn([]), 2])'}") public void greatest_invalidDynArgs_throwsRuntimeException(String expr) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); - - CelEvaluationException e = - assertThrows(CelEvaluationException.class, () -> CEL_RUNTIME.createProgram(ast).eval()); + CelEvaluationException e = assertThrows(CelEvaluationException.class, () -> eval(expr)); - assertThat(e).hasMessageThat().contains("Function 'math_@max_list_dyn' failed with arg(s)"); + assertThat(e).hasMessageThat().contains("failed with arg(s)"); } @Test public void greatest_listVariableIsEmpty_throwsRuntimeException() throws Exception { CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder() - .addLibraries(CelExtensions.math(CEL_OPTIONS)) + .addLibraries(CelExtensions.math()) .addVar("listVar", ListType.create(SimpleType.INT)) .build(); CelAbstractSyntaxTree ast = celCompiler.compile("math.greatest(listVar)").getAst(); @@ -332,12 +294,9 @@ public void greatest_listVariableIsEmpty_throwsRuntimeException() throws Excepti CelEvaluationException e = assertThrows( CelEvaluationException.class, - () -> - CEL_RUNTIME - .createProgram(ast) - .eval(ImmutableMap.of("listVar", ImmutableList.of()))); + () -> cel.createProgram(ast).eval(ImmutableMap.of("listVar", ImmutableList.of()))); - assertThat(e).hasMessageThat().contains("Function 'math_@max_list_dyn' failed with arg(s)"); + assertThat(e).hasMessageThat().contains("failed with arg(s)"); assertThat(e) .hasCauseThat() .hasMessageThat() @@ -347,25 +306,25 @@ public void greatest_listVariableIsEmpty_throwsRuntimeException() throws Excepti @Test @TestParameters("{expr: '100.greatest(1) == 1'}") @TestParameters("{expr: 'dyn(100).greatest(1) == 1'}") - public void greatest_nonProtoNamespace_success(String expr) throws Exception { - CelCompiler celCompiler = - CelCompilerFactory.standardCelCompilerBuilder() - .addLibraries(CelExtensions.math(CEL_OPTIONS)) + public void greatest_nonMathNamespace_success(String expr) throws Exception { + Cel cel = + runtimeFlavor + .builder() + .addCompilerLibraries(CelExtensions.math()) + .addRuntimeLibraries(CelExtensions.math()) .addFunctionDeclarations( CelFunctionDecl.newFunctionDeclaration( "greatest", CelOverloadDecl.newMemberOverload( "int_greatest_int", SimpleType.INT, SimpleType.INT, SimpleType.INT))) - .build(); - CelRuntime celRuntime = - CelRuntimeFactory.standardCelRuntimeBuilder() .addFunctionBindings( - CelFunctionBinding.from( - "int_greatest_int", Long.class, Long.class, (arg1, arg2) -> arg2)) + CelFunctionBinding.fromOverloads( + "greatest", + CelFunctionBinding.from( + "int_greatest_int", Long.class, Long.class, (arg1, arg2) -> arg2))) .build(); - CelAbstractSyntaxTree ast = celCompiler.compile(expr).getAst(); - boolean result = (boolean) celRuntime.createProgram(ast).eval(); + boolean result = (boolean) eval(cel, expr); assertThat(result).isTrue(); } @@ -400,13 +359,14 @@ public void greatest_nonProtoNamespace_success(String expr) throws Exception { "{expr: 'math.least(-9223372036854775808, 10, 3u, -5.0, 0)', expectedResult:" + " -9223372036854775808}") @TestParameters("{expr: 'math.least([5.4, -10, 3u, -5.0, 3.5])', expectedResult: -10}") + @TestParameters("{expr: 'math.least(1, 9223372036854775807u)', expectedResult: 1}") + @TestParameters("{expr: 'math.least(9223372036854775807u, 1)', expectedResult: 1}") + @TestParameters("{expr: 'math.least(9223372036854775807, 10, 3u, 5.0, 0)', expectedResult: 0}") @TestParameters( "{expr: 'math.least([dyn(5.4), dyn(-10), dyn(3u), dyn(-5.0), dyn(3.5)])', expectedResult:" + " -10}") public void least_intResult_success(String expr, long expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); - - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = eval(expr); assertThat(result).isEqualTo(expectedResult); } @@ -443,9 +403,7 @@ public void least_intResult_success(String expr, long expectedResult) throws Exc "{expr: 'math.least([dyn(5.4), dyn(10.0), dyn(3u), dyn(-5.0), dyn(3.5)])', expectedResult:" + " -5.0}") public void least_doubleResult_success(String expr, double expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); - - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = eval(expr); assertThat(result).isEqualTo(expectedResult); } @@ -474,12 +432,12 @@ public void least_doubleResult_withUnsignedLongsEnabled_success( CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder() .setOptions(celOptions) - .addLibraries(CelExtensions.math(celOptions)) + .addLibraries(CelExtensions.math()) .build(); CelRuntime celRuntime = CelRuntimeFactory.standardCelRuntimeBuilder() .setOptions(celOptions) - .addLibraries(CelExtensions.math(celOptions)) + .addLibraries(CelExtensions.math()) .build(); CelAbstractSyntaxTree ast = celCompiler.compile(expr).getAst(); @@ -489,37 +447,15 @@ public void least_doubleResult_withUnsignedLongsEnabled_success( } @Test - @TestParameters("{expr: 'math.least(5u)', expectedResult: 5}") - @TestParameters("{expr: 'math.least(1u, 1.0)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(1u, 1)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(1u, 1u)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(3u, 3.0)', expectedResult: 3}") - @TestParameters("{expr: 'math.least(9u, 10u)', expectedResult: 9}") - @TestParameters("{expr: 'math.least(15u, 14u)', expectedResult: 14}") - @TestParameters("{expr: 'math.least(1, 9223372036854775807u)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(9223372036854775807u, 1)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(1u, 1, 1)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(3u, 1u, 10u)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(1u, 5u, 2u)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(9, 1u, 0u)', expectedResult: 0}") - @TestParameters("{expr: 'math.least(dyn(1u), 1, 1.0)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(5.0, 1u, 3u)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(5.4, 1u, 3u, 9, 3.5)', expectedResult: 1}") - @TestParameters("{expr: 'math.least(5.4, 10, 3u, 5.0, 9223372036854775807)', expectedResult: 3}") - @TestParameters("{expr: 'math.least(9223372036854775807, 10, 3u, 5.0, 0)', expectedResult: 0}") - @TestParameters("{expr: 'math.least([5.4, 10, 3u, 5.0, 3.5])', expectedResult: 3}") + @TestParameters("{expr: 'math.least(9, 1u, 0u)', expectedResult: '0'}") + @TestParameters("{expr: 'math.least(dyn(1u), 1, 1.0)', expectedResult: '1'}") + @TestParameters("{expr: 'math.least(5.0, 1u, 3u)', expectedResult: '1'}") + @TestParameters("{expr: 'math.least(5.4, 1u, 3u, 9, 3.5)', expectedResult: '1'}") @TestParameters( - "{expr: 'math.least([dyn(5.4), dyn(10), dyn(3u), dyn(5.0), dyn(3.5)])', expectedResult: 3}") - public void least_unsignedLongResult_withSignedLongType_success(String expr, long expectedResult) - throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); - - Object result = CEL_RUNTIME.createProgram(ast).eval(); - - assertThat(result).isEqualTo(expectedResult); - } - - @Test + "{expr: 'math.least(5.4, 10, 3u, 5.0, 9223372036854775807)', expectedResult: '3'}") + @TestParameters("{expr: 'math.least([5.4, 10, 3u, 5.0, 3.5])', expectedResult: '3'}") + @TestParameters( + "{expr: 'math.least([dyn(5.4), dyn(10), dyn(3u), dyn(5.0), dyn(3.5)])', expectedResult: '3'}") @TestParameters( "{expr: 'math.least(18446744073709551615u)', expectedResult: '18446744073709551615'}") @TestParameters("{expr: 'math.least(1u, 1.0)', expectedResult: '1'}") @@ -553,12 +489,12 @@ public void least_unsignedLongResult_withUnsignedLongType_success( CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder() .setOptions(celOptions) - .addLibraries(CelExtensions.math(celOptions)) + .addLibraries(CelExtensions.math()) .build(); CelRuntime celRuntime = CelRuntimeFactory.standardCelRuntimeBuilder() .setOptions(celOptions) - .addLibraries(CelExtensions.math(celOptions)) + .addLibraries(CelExtensions.math()) .build(); CelAbstractSyntaxTree ast = celCompiler.compile(expr).getAst(); @@ -569,9 +505,9 @@ public void least_unsignedLongResult_withUnsignedLongType_success( @Test public void least_noArgs_throwsCompilationException() { + Assume.assumeFalse(isParseOnly); CelValidationException e = - assertThrows( - CelValidationException.class, () -> CEL_COMPILER.compile("math.least()").getAst()); + assertThrows(CelValidationException.class, () -> cel.compile("math.least()").getAst()); assertThat(e).hasMessageThat().contains("math.least() requires at least one argument"); } @@ -581,8 +517,9 @@ public void least_noArgs_throwsCompilationException() { @TestParameters("{expr: 'math.least({})'}") @TestParameters("{expr: 'math.least([])'}") public void least_invalidSingleArg_throwsCompilationException(String expr) { + Assume.assumeFalse(isParseOnly); CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("math.least() invalid single argument value"); } @@ -595,8 +532,9 @@ public void least_invalidSingleArg_throwsCompilationException(String expr) { @TestParameters("{expr: 'math.least([1, {}, 2])'}") @TestParameters("{expr: 'math.least([1, [], 2])'}") public void least_invalidArgs_throwsCompilationException(String expr) { + Assume.assumeFalse(isParseOnly); CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e) .hasMessageThat() @@ -610,19 +548,16 @@ public void least_invalidArgs_throwsCompilationException(String expr) { @TestParameters("{expr: 'math.least([1, dyn({}), 2])'}") @TestParameters("{expr: 'math.least([1, dyn([]), 2])'}") public void least_invalidDynArgs_throwsRuntimeException(String expr) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); - - CelEvaluationException e = - assertThrows(CelEvaluationException.class, () -> CEL_RUNTIME.createProgram(ast).eval()); + CelEvaluationException e = assertThrows(CelEvaluationException.class, () -> eval(expr)); - assertThat(e).hasMessageThat().contains("Function 'math_@min_list_dyn' failed with arg(s)"); + assertThat(e).hasMessageThat().contains("failed with arg(s)"); } @Test public void least_listVariableIsEmpty_throwsRuntimeException() throws Exception { CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder() - .addLibraries(CelExtensions.math(CEL_OPTIONS)) + .addLibraries(CelExtensions.math()) .addVar("listVar", ListType.create(SimpleType.INT)) .build(); CelAbstractSyntaxTree ast = celCompiler.compile("math.least(listVar)").getAst(); @@ -630,12 +565,9 @@ public void least_listVariableIsEmpty_throwsRuntimeException() throws Exception CelEvaluationException e = assertThrows( CelEvaluationException.class, - () -> - CEL_RUNTIME - .createProgram(ast) - .eval(ImmutableMap.of("listVar", ImmutableList.of()))); + () -> cel.createProgram(ast).eval(ImmutableMap.of("listVar", ImmutableList.of()))); - assertThat(e).hasMessageThat().contains("Function 'math_@min_list_dyn' failed with arg(s)"); + assertThat(e).hasMessageThat().contains("failed with arg(s)"); assertThat(e) .hasCauseThat() .hasMessageThat() @@ -645,24 +577,25 @@ public void least_listVariableIsEmpty_throwsRuntimeException() throws Exception @Test @TestParameters("{expr: '100.least(1) == 1'}") @TestParameters("{expr: 'dyn(100).least(1) == 1'}") - public void least_nonProtoNamespace_success(String expr) throws Exception { - CelCompiler celCompiler = - CelCompilerFactory.standardCelCompilerBuilder() - .addLibraries(CelExtensions.math(CEL_OPTIONS)) + public void least_nonMathNamespace_success(String expr) throws Exception { + Cel cel = + runtimeFlavor + .builder() + .addCompilerLibraries(CelExtensions.math()) + .addRuntimeLibraries(CelExtensions.math()) .addFunctionDeclarations( CelFunctionDecl.newFunctionDeclaration( "least", CelOverloadDecl.newMemberOverload( "int_least", SimpleType.INT, SimpleType.INT, SimpleType.INT))) - .build(); - CelRuntime celRuntime = - CelRuntimeFactory.standardCelRuntimeBuilder() .addFunctionBindings( - CelFunctionBinding.from("int_least", Long.class, Long.class, (arg1, arg2) -> arg2)) + CelFunctionBinding.fromOverloads( + "least", + CelFunctionBinding.from( + "int_least", Long.class, Long.class, (arg1, arg2) -> arg2))) .build(); - CelAbstractSyntaxTree ast = celCompiler.compile(expr).getAst(); - boolean result = (boolean) celRuntime.createProgram(ast).eval(); + boolean result = (boolean) eval(cel, expr); assertThat(result).isTrue(); } @@ -676,9 +609,9 @@ public void least_nonProtoNamespace_success(String expr) throws Exception { @TestParameters("{expr: 'math.isNaN(math.sign(0.0/0.0))', expectedResult: true}") @TestParameters("{expr: 'math.isNaN(math.sqrt(-4))', expectedResult: true}") public void isNaN_success(String expr, boolean expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -690,7 +623,7 @@ public void isNaN_success(String expr, boolean expectedResult) throws Exception @TestParameters("{expr: 'math.isNaN(1u)'}") public void isNaN_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.isNaN'"); } @@ -701,9 +634,9 @@ public void isNaN_invalidArgs_throwsException(String expr) { @TestParameters("{expr: 'math.isFinite(1.0/0.0)', expectedResult: false}") @TestParameters("{expr: 'math.isFinite(0.0/0.0)', expectedResult: false}") public void isFinite_success(String expr, boolean expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -715,7 +648,7 @@ public void isFinite_success(String expr, boolean expectedResult) throws Excepti @TestParameters("{expr: 'math.isFinite(1u)'}") public void isFinite_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.isFinite'"); } @@ -726,9 +659,9 @@ public void isFinite_invalidArgs_throwsException(String expr) { @TestParameters("{expr: 'math.isInf(0.0/0.0)', expectedResult: false}") @TestParameters("{expr: 'math.isInf(10.0)', expectedResult: false}") public void isInf_success(String expr, boolean expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -740,7 +673,7 @@ public void isInf_success(String expr, boolean expectedResult) throws Exception @TestParameters("{expr: 'math.isInf(1u)'}") public void isInf_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.isInf'"); } @@ -752,9 +685,9 @@ public void isInf_invalidArgs_throwsException(String expr) { @TestParameters("{expr: 'math.ceil(20.0)' , expectedResult: 20.0}") @TestParameters("{expr: 'math.ceil(0.0/0.0)' , expectedResult: NaN}") public void ceil_success(String expr, double expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -766,7 +699,7 @@ public void ceil_success(String expr, double expectedResult) throws Exception { @TestParameters("{expr: 'math.ceil(1u)'}") public void ceil_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.ceil'"); } @@ -777,9 +710,9 @@ public void ceil_invalidArgs_throwsException(String expr) { @TestParameters("{expr: 'math.floor(0.0/0.0)' , expectedResult: NaN}") @TestParameters("{expr: 'math.floor(50.0)' , expectedResult: 50.0}") public void floor_success(String expr, double expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -791,7 +724,7 @@ public void floor_success(String expr, double expectedResult) throws Exception { @TestParameters("{expr: 'math.floor(1u)'}") public void floor_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.floor'"); } @@ -806,9 +739,9 @@ public void floor_invalidArgs_throwsException(String expr) { @TestParameters("{expr: 'math.round(1.0/0.0)' , expectedResult: Infinity}") @TestParameters("{expr: 'math.round(-1.0/0.0)' , expectedResult: -Infinity}") public void round_success(String expr, double expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -820,7 +753,7 @@ public void round_success(String expr, double expectedResult) throws Exception { @TestParameters("{expr: 'math.round(1u)'}") public void round_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.round'"); } @@ -832,9 +765,9 @@ public void round_invalidArgs_throwsException(String expr) { @TestParameters("{expr: 'math.trunc(1.0/0.0)' , expectedResult: Infinity}") @TestParameters("{expr: 'math.trunc(-1.0/0.0)' , expectedResult: -Infinity}") public void trunc_success(String expr, double expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -846,7 +779,7 @@ public void trunc_success(String expr, double expectedResult) throws Exception { @TestParameters("{expr: 'math.trunc(1u)'}") public void trunc_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.trunc'"); } @@ -856,9 +789,9 @@ public void trunc_invalidArgs_throwsException(String expr) { @TestParameters("{expr: 'math.abs(-1657643)', expectedResult: 1657643}") @TestParameters("{expr: 'math.abs(-2147483648)', expectedResult: 2147483648}") public void abs_intResult_success(String expr, long expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -871,9 +804,9 @@ public void abs_intResult_success(String expr, long expectedResult) throws Excep @TestParameters("{expr: 'math.abs(1.0/0.0)' , expectedResult: Infinity}") @TestParameters("{expr: 'math.abs(-1.0/0.0)' , expectedResult: Infinity}") public void abs_doubleResult_success(String expr, double expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -883,7 +816,7 @@ public void abs_overflow_throwsException() { CelValidationException e = assertThrows( CelValidationException.class, - () -> CEL_COMPILER.compile("math.abs(-9223372036854775809)").getAst()); + () -> cel.compile("math.abs(-9223372036854775809)").getAst()); assertThat(e) .hasMessageThat() @@ -896,9 +829,9 @@ public void abs_overflow_throwsException() { @TestParameters("{expr: 'math.sign(-0)', expectedResult: 0}") @TestParameters("{expr: 'math.sign(11213)', expectedResult: 1}") public void sign_intResult_success(String expr, int expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -914,9 +847,9 @@ public void sign_intResult_success(String expr, int expectedResult) throws Excep @TestParameters("{expr: 'math.sign(1.0/0.0)' , expectedResult: 1.0}") @TestParameters("{expr: 'math.sign(-1.0/0.0)' , expectedResult: -1.0}") public void sign_doubleResult_success(String expr, double expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -926,7 +859,7 @@ public void sign_doubleResult_success(String expr, double expectedResult) throws @TestParameters("{expr: 'math.sign(\"\")'}") public void sign_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.sign'"); } @@ -938,9 +871,9 @@ public void sign_invalidArgs_throwsException(String expr) { "{expr: 'math.bitAnd(9223372036854775807,9223372036854775807)' , expectedResult:" + " 9223372036854775807}") public void bitAnd_signedInt_success(String expr, long expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -950,9 +883,9 @@ public void bitAnd_signedInt_success(String expr, long expectedResult) throws Ex @TestParameters("{expr: 'math.bitAnd(1u,3u)' , expectedResult: 1}") public void bitAnd_unSignedInt_success(String expr, UnsignedLong expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_UNSIGNED_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_UNSIGNED_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -963,7 +896,7 @@ public void bitAnd_unSignedInt_success(String expr, UnsignedLong expectedResult) @TestParameters("{expr: 'math.bitAnd(1)'}") public void bitAnd_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.bitAnd'"); } @@ -973,10 +906,7 @@ public void bitAnd_maxValArg_throwsException() { CelValidationException e = assertThrows( CelValidationException.class, - () -> - CEL_COMPILER - .compile("math.bitAnd(9223372036854775807,9223372036854775809)") - .getAst()); + () -> cel.compile("math.bitAnd(9223372036854775807,9223372036854775809)").getAst()); assertThat(e) .hasMessageThat() @@ -987,9 +917,9 @@ public void bitAnd_maxValArg_throwsException() { @TestParameters("{expr: 'math.bitOr(1,2)' , expectedResult: 3}") @TestParameters("{expr: 'math.bitOr(1,-1)' , expectedResult: -1}") public void bitOr_signedInt_success(String expr, long expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -998,9 +928,9 @@ public void bitOr_signedInt_success(String expr, long expectedResult) throws Exc @TestParameters("{expr: 'math.bitOr(1u,2u)' , expectedResult: 3}") @TestParameters("{expr: 'math.bitOr(1090u,3u)' , expectedResult: 1091}") public void bitOr_unSignedInt_success(String expr, UnsignedLong expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_UNSIGNED_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_UNSIGNED_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -1011,7 +941,7 @@ public void bitOr_unSignedInt_success(String expr, UnsignedLong expectedResult) @TestParameters("{expr: 'math.bitOr(1)'}") public void bitOr_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.bitOr'"); } @@ -1020,9 +950,9 @@ public void bitOr_invalidArgs_throwsException(String expr) { @TestParameters("{expr: 'math.bitXor(1,2)' , expectedResult: 3}") @TestParameters("{expr: 'math.bitXor(3,5)' , expectedResult: 6}") public void bitXor_signedInt_success(String expr, long expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -1032,9 +962,9 @@ public void bitXor_signedInt_success(String expr, long expectedResult) throws Ex @TestParameters("{expr: 'math.bitXor(3u, 5u)' , expectedResult: 6}") public void bitXor_unSignedInt_success(String expr, UnsignedLong expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_UNSIGNED_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_UNSIGNED_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -1045,7 +975,7 @@ public void bitXor_unSignedInt_success(String expr, UnsignedLong expectedResult) @TestParameters("{expr: 'math.bitXor(1)'}") public void bitXor_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.bitXor'"); } @@ -1055,9 +985,9 @@ public void bitXor_invalidArgs_throwsException(String expr) { @TestParameters("{expr: 'math.bitNot(0)' , expectedResult: -1}") @TestParameters("{expr: 'math.bitNot(-1)' , expectedResult: 0}") public void bitNot_signedInt_success(String expr, long expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -1067,9 +997,9 @@ public void bitNot_signedInt_success(String expr, long expectedResult) throws Ex @TestParameters("{expr: 'math.bitNot(12310u)' , expectedResult: 18446744073709539305}") public void bitNot_unSignedInt_success(String expr, UnsignedLong expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_UNSIGNED_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_UNSIGNED_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -1080,7 +1010,7 @@ public void bitNot_unSignedInt_success(String expr, UnsignedLong expectedResult) @TestParameters("{expr: 'math.bitNot(\"\")'}") public void bitNot_invalidArgs_throwsException(String expr) { CelValidationException e = - assertThrows(CelValidationException.class, () -> CEL_COMPILER.compile(expr).getAst()); + assertThrows(CelValidationException.class, () -> cel.compile(expr).getAst()); assertThat(e).hasMessageThat().contains("found no matching overload for 'math.bitNot'"); } @@ -1090,9 +1020,9 @@ public void bitNot_invalidArgs_throwsException(String expr) { @TestParameters("{expr: 'math.bitShiftLeft(12121, 11)' , expectedResult: 24823808}") @TestParameters("{expr: 'math.bitShiftLeft(-1, 64)' , expectedResult: 0}") public void bitShiftLeft_signedInt_success(String expr, long expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -1103,9 +1033,9 @@ public void bitShiftLeft_signedInt_success(String expr, long expectedResult) thr @TestParameters("{expr: 'math.bitShiftLeft(1u, 65)' , expectedResult: 0}") public void bitShiftLeft_unSignedInt_success(String expr, UnsignedLong expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_UNSIGNED_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_UNSIGNED_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -1114,11 +1044,10 @@ public void bitShiftLeft_unSignedInt_success(String expr, UnsignedLong expectedR @TestParameters("{expr: 'math.bitShiftLeft(1, -2)'}") @TestParameters("{expr: 'math.bitShiftLeft(1u, -2)'}") public void bitShiftLeft_invalidArgs_throwsException(String expr) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); CelEvaluationException e = - assertThrows( - CelEvaluationException.class, () -> CEL_UNSIGNED_RUNTIME.createProgram(ast).eval()); + assertThrows(CelEvaluationException.class, () -> cel.createProgram(ast).eval()); assertThat(e).hasMessageThat().contains("evaluation error"); assertThat(e).hasCauseThat().isInstanceOf(IllegalArgumentException.class); @@ -1131,9 +1060,9 @@ public void bitShiftLeft_invalidArgs_throwsException(String expr) throws Excepti @TestParameters("{expr: 'math.bitShiftRight(12121, 11)' , expectedResult: 5}") @TestParameters("{expr: 'math.bitShiftRight(-1, 64)' , expectedResult: 0}") public void bitShiftRight_signedInt_success(String expr, long expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); + CelAbstractSyntaxTree ast = cel.compile(expr).getAst(); - Object result = CEL_RUNTIME.createProgram(ast).eval(); + Object result = cel.createProgram(ast).eval(); assertThat(result).isEqualTo(expectedResult); } @@ -1144,9 +1073,7 @@ public void bitShiftRight_signedInt_success(String expr, long expectedResult) th @TestParameters("{expr: 'math.bitShiftRight(1u, 65)' , expectedResult: 0}") public void bitShiftRight_unSignedInt_success(String expr, UnsignedLong expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_UNSIGNED_COMPILER.compile(expr).getAst(); - - Object result = CEL_UNSIGNED_RUNTIME.createProgram(ast).eval(); + Object result = eval(expr); assertThat(result).isEqualTo(expectedResult); } @@ -1155,11 +1082,7 @@ public void bitShiftRight_unSignedInt_success(String expr, UnsignedLong expected @TestParameters("{expr: 'math.bitShiftRight(23111u, -212)'}") @TestParameters("{expr: 'math.bitShiftRight(23, -212)'}") public void bitShiftRight_invalidArgs_throwsException(String expr) throws Exception { - CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expr).getAst(); - - CelEvaluationException e = - assertThrows( - CelEvaluationException.class, () -> CEL_UNSIGNED_RUNTIME.createProgram(ast).eval()); + CelEvaluationException e = assertThrows(CelEvaluationException.class, () -> eval(expr)); assertThat(e).hasMessageThat().contains("evaluation error"); assertThat(e).hasCauseThat().isInstanceOf(IllegalArgumentException.class); @@ -1174,10 +1097,26 @@ public void bitShiftRight_invalidArgs_throwsException(String expr) throws Except @TestParameters("{expr: 'math.sqrt(1.0/0.0)', expectedResult: Infinity}") @TestParameters("{expr: 'math.sqrt(-1)', expectedResult: NaN}") public void sqrt_success(String expr, double expectedResult) throws Exception { - CelAbstractSyntaxTree ast = CEL_UNSIGNED_COMPILER.compile(expr).getAst(); - - Object result = CEL_UNSIGNED_RUNTIME.createProgram(ast).eval(); + Object result = eval(expr); assertThat(result).isEqualTo(expectedResult); } + + private Object eval(Cel cel, String expression, Map variables) throws Exception { + CelAbstractSyntaxTree ast; + if (isParseOnly) { + ast = cel.parse(expression).getAst(); + } else { + ast = cel.compile(expression).getAst(); + } + return cel.createProgram(ast).eval(variables); + } + + private Object eval(Cel celInstance, String expression) throws Exception { + return eval(celInstance, expression, ImmutableMap.of()); + } + + private Object eval(String expression) throws Exception { + return eval(this.cel, expression, ImmutableMap.of()); + } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelProtoExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelProtoExtensionsTest.java index 2e55619db..f46ea5b1a 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelProtoExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelProtoExtensionsTest.java @@ -26,7 +26,6 @@ import com.google.testing.junit.testparameterinjector.TestParameterInjector; import com.google.testing.junit.testparameterinjector.TestParameters; import dev.cel.bundle.Cel; -import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelContainer; import dev.cel.common.CelFunctionDecl; import dev.cel.common.CelOptions; @@ -41,34 +40,23 @@ import dev.cel.parser.CelMacro; import dev.cel.parser.CelStandardMacro; import dev.cel.runtime.CelFunctionBinding; -import dev.cel.testing.CelRuntimeFlavor; -import java.util.Map; import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TestParameterInjector.class) -public final class CelProtoExtensionsTest { - - @TestParameter public CelRuntimeFlavor runtimeFlavor; - @TestParameter public boolean isParseOnly; - - private Cel cel; - - @Before - public void setUp() { - // Legacy runtime does not support parsed-only evaluation mode. - Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); - this.cel = - runtimeFlavor - .builder() - .addCompilerLibraries(CelExtensions.protos()) - .setStandardMacros(CelStandardMacro.STANDARD_MACROS) - .addFileTypes(TestAllTypesExtensions.getDescriptor()) - .addVar("msg", StructTypeReference.create("cel.expr.conformance.proto2.TestAllTypes")) - .setContainer(CelContainer.ofName("cel.expr.conformance.proto2")) - .build(); +public final class CelProtoExtensionsTest extends CelExtensionTestBase { + + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .addCompilerLibraries(CelExtensions.protos()) + .setStandardMacros(CelStandardMacro.STANDARD_MACROS) + .addFileTypes(TestAllTypesExtensions.getDescriptor()) + .addVar("msg", StructTypeReference.create("cel.expr.conformance.proto2.TestAllTypes")) + .setContainer(CelContainer.ofName("cel.expr.conformance.proto2")) + .build(); } private static final TestAllTypes PACKAGE_SCOPED_EXT_MSG = @@ -342,13 +330,5 @@ public void parseErrors(@TestParameter ParseErrorTestCase testcase) { assertThat(e).hasMessageThat().isEqualTo(testcase.error); } - private Object eval(String expression, Map variables) throws Exception { - return eval(this.cel, expression, variables); - } - private Object eval(Cel cel, String expression, Map variables) throws Exception { - CelAbstractSyntaxTree ast = - this.isParseOnly ? cel.parse(expression).getAst() : cel.compile(expression).getAst(); - return cel.createProgram(ast).eval(variables); - } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelRegexExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelRegexExtensionsTest.java index 924344b25..97d0cc90c 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelRegexExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelRegexExtensionsTest.java @@ -21,35 +21,23 @@ import com.google.testing.junit.testparameterinjector.TestParameterInjector; import com.google.testing.junit.testparameterinjector.TestParameters; import dev.cel.bundle.Cel; -import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelFunctionDecl; import dev.cel.common.CelOptions; import dev.cel.runtime.CelEvaluationException; -import dev.cel.testing.CelRuntimeFlavor; import java.util.Optional; -import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TestParameterInjector.class) -public final class CelRegexExtensionsTest { - - @TestParameter public CelRuntimeFlavor runtimeFlavor; - @TestParameter public boolean isParseOnly; - - private Cel cel; - - @Before - public void setUp() { - // Legacy runtime does not support parsed-only evaluation mode. - Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); - this.cel = - runtimeFlavor - .builder() - .addCompilerLibraries(CelExtensions.regex()) - .addRuntimeLibraries(CelExtensions.regex()) - .build(); +public final class CelRegexExtensionsTest extends CelExtensionTestBase { + + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .addCompilerLibraries(CelExtensions.regex()) + .addRuntimeLibraries(CelExtensions.regex()) + .build(); } @@ -276,9 +264,5 @@ public void extractAll_multipleCaptureGroups_throwsException(String target, Stri .contains("Regular expression has more than one capturing group:"); } - private Object eval(String expr) throws Exception { - CelAbstractSyntaxTree ast = - isParseOnly ? cel.parse(expr).getAst() : cel.compile(expr).getAst(); - return cel.createProgram(ast).eval(); - } + } diff --git a/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java index 9007bba2e..091d456f5 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelSetsExtensionsTest.java @@ -23,7 +23,6 @@ import com.google.testing.junit.testparameterinjector.TestParameterInjector; import com.google.testing.junit.testparameterinjector.TestParameters; import dev.cel.bundle.Cel; -import dev.cel.bundle.CelBuilder; import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelContainer; import dev.cel.common.CelFunctionDecl; @@ -39,27 +38,39 @@ import dev.cel.runtime.CelRuntime; import dev.cel.testing.CelRuntimeFlavor; import java.util.List; -import java.util.Map; import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TestParameterInjector.class) -public final class CelSetsExtensionsTest { +public final class CelSetsExtensionsTest extends CelExtensionTestBase { private static final CelOptions CEL_OPTIONS = CelOptions.current().enableHeterogeneousNumericComparisons(true).build(); - @TestParameter public CelRuntimeFlavor runtimeFlavor; - @TestParameter public boolean isParseOnly; - - private Cel cel; - - @Before - public void setUp() { - // Legacy runtime does not support parsed-only evaluation mode. - Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); - this.cel = setupEnv(runtimeFlavor.builder()); + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .addMessageTypes(TestAllTypes.getDescriptor()) + .setOptions(CEL_OPTIONS) + .setContainer(CelContainer.ofName("cel.expr.conformance.proto3")) + .addCompilerLibraries(CelExtensions.sets(CEL_OPTIONS)) + .addRuntimeLibraries(CelExtensions.sets(CEL_OPTIONS)) + .addVar("list", ListType.create(SimpleType.INT)) + .addVar("subList", ListType.create(SimpleType.INT)) + .addFunctionDeclarations( + CelFunctionDecl.newFunctionDeclaration( + "new_int", + CelOverloadDecl.newGlobalOverload("new_int_int64", SimpleType.INT, SimpleType.INT))) + .addFunctionBindings( + CelFunctionBinding.fromOverloads( + "new_int", + CelFunctionBinding.from( + "new_int_int64", + Long.class, + // Intentionally return java.lang.Integer to test primitive type adaptation + Math::toIntExact))) + .build(); } @Test @@ -375,7 +386,7 @@ public void setsExtension_containsFunctionSubset_succeeds() throws Exception { .addRuntimeLibraries(setsExtensions) .build(); - Object evaluatedResult = eval(cel, "sets.contains([1, 2], [2])", ImmutableMap.of()); + Object evaluatedResult = eval(cel, "sets.contains([1, 2], [2])"); assertThat(evaluatedResult).isEqualTo(true); } @@ -391,7 +402,7 @@ public void setsExtension_equivalentFunctionSubset_succeeds() throws Exception { .addRuntimeLibraries(setsExtensions) .build(); - Object evaluatedResult = eval(cel, "sets.equivalent([1, 1], [1])", ImmutableMap.of()); + Object evaluatedResult = eval(cel, "sets.equivalent([1, 1], [1])"); assertThat(evaluatedResult).isEqualTo(true); } @@ -407,7 +418,7 @@ public void setsExtension_intersectsFunctionSubset_succeeds() throws Exception { .addRuntimeLibraries(setsExtensions) .build(); - Object evaluatedResult = eval(cel, "sets.intersects([1, 1], [1])", ImmutableMap.of()); + Object evaluatedResult = eval(cel, "sets.intersects([1, 1], [1])"); assertThat(evaluatedResult).isEqualTo(true); } @@ -450,45 +461,5 @@ public void setsExtension_evaluateUnallowedFunction_throws() throws Exception { } } - private Object eval(Cel cel, String expression, Map variables) throws Exception { - CelAbstractSyntaxTree ast; - if (isParseOnly) { - ast = cel.parse(expression).getAst(); - } else { - ast = cel.compile(expression).getAst(); - } - return cel.createProgram(ast).eval(variables); - } - - private Object eval(String expression) throws Exception { - return eval(this.cel, expression, ImmutableMap.of()); - } - - private Object eval(String expression, Map variables) throws Exception { - return eval(this.cel, expression, variables); - } - private static Cel setupEnv(CelBuilder celBuilder) { - return celBuilder - .addMessageTypes(TestAllTypes.getDescriptor()) - .setOptions(CEL_OPTIONS) - .setContainer(CelContainer.ofName("cel.expr.conformance.proto3")) - .addCompilerLibraries(CelExtensions.sets(CEL_OPTIONS)) - .addRuntimeLibraries(CelExtensions.sets(CEL_OPTIONS)) - .addVar("list", ListType.create(SimpleType.INT)) - .addVar("subList", ListType.create(SimpleType.INT)) - .addFunctionDeclarations( - CelFunctionDecl.newFunctionDeclaration( - "new_int", - CelOverloadDecl.newGlobalOverload("new_int_int64", SimpleType.INT, SimpleType.INT))) - .addFunctionBindings( - CelFunctionBinding.fromOverloads( - "new_int", - CelFunctionBinding.from( - "new_int_int64", - Long.class, - // Intentionally return java.lang.Integer to test primitive type adaptation - Math::toIntExact))) - .build(); - } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java index e7542b7b7..4b242ddcd 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java @@ -33,40 +33,29 @@ import dev.cel.extensions.CelStringExtensions.Function; import dev.cel.runtime.CelEvaluationException; import dev.cel.runtime.CelRuntime; -import dev.cel.testing.CelRuntimeFlavor; import java.util.List; -import java.util.Map; import org.junit.Assume; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TestParameterInjector.class) -public final class CelStringExtensionsTest { - - @TestParameter public CelRuntimeFlavor runtimeFlavor; - @TestParameter public boolean isParseOnly; - - private Cel cel; - - @Before - public void setUp() { - // Legacy runtime does not support parsed-only evaluation mode. - Assume.assumeFalse(runtimeFlavor.equals(CelRuntimeFlavor.LEGACY) && isParseOnly); - this.cel = - runtimeFlavor - .builder() - .addCompilerLibraries(CelExtensions.strings()) - .addRuntimeLibraries(CelExtensions.strings()) - .addVar("s", SimpleType.STRING) - .addVar("separator", SimpleType.STRING) - .addVar("index", SimpleType.INT) - .addVar("offset", SimpleType.INT) - .addVar("indexOfParam", SimpleType.STRING) - .addVar("beginIndex", SimpleType.INT) - .addVar("endIndex", SimpleType.INT) - .addVar("limit", SimpleType.INT) - .build(); +public final class CelStringExtensionsTest extends CelExtensionTestBase { + + @Override + protected Cel newCelEnv() { + return runtimeFlavor + .builder() + .addCompilerLibraries(CelExtensions.strings()) + .addRuntimeLibraries(CelExtensions.strings()) + .addVar("s", SimpleType.STRING) + .addVar("separator", SimpleType.STRING) + .addVar("index", SimpleType.INT) + .addVar("offset", SimpleType.INT) + .addVar("indexOfParam", SimpleType.STRING) + .addVar("beginIndex", SimpleType.INT) + .addVar("endIndex", SimpleType.INT) + .addVar("limit", SimpleType.INT) + .build(); } @Test @@ -388,13 +377,10 @@ public void split_withLimit_separatorIsNonString_throwsException() { @Test public void split_withLimitOverflow_throwsException() throws Exception { + ImmutableMap variables = ImmutableMap.of("limit", 2147483648L); // INT_MAX + 1 CelEvaluationException exception = assertThrows( - CelEvaluationException.class, - () -> - eval( - "'test'.split('', limit)", - ImmutableMap.of("limit", 2147483648L))); // INT_MAX + 1 + CelEvaluationException.class, () -> eval("'test'.split('', limit)", variables)); assertThat(exception) .hasMessageThat() @@ -454,13 +440,10 @@ public void substring_beginAndEndIndex_unicode_success( @TestParameters("{string: '', beginIndex: 2}") public void substring_beginIndexOutOfRange_ascii_throwsException(String string, int beginIndex) throws Exception { + ImmutableMap variables = ImmutableMap.of("s", string, "beginIndex", beginIndex); CelEvaluationException exception = assertThrows( - CelEvaluationException.class, - () -> - eval( - "s.substring(beginIndex)", - ImmutableMap.of("s", string, "beginIndex", beginIndex))); + CelEvaluationException.class, () -> eval("s.substring(beginIndex)", variables)); String exceptionMessage = String.format( @@ -478,13 +461,10 @@ public void substring_beginIndexOutOfRange_ascii_throwsException(String string, @TestParameters("{string: '😁가나', beginIndex: 4, uniqueCharCount: 3}") public void substring_beginIndexOutOfRange_unicode_throwsException( String string, int beginIndex, int uniqueCharCount) throws Exception { + ImmutableMap variables = ImmutableMap.of("s", string, "beginIndex", beginIndex); CelEvaluationException exception = assertThrows( - CelEvaluationException.class, - () -> - eval( - "s.substring(beginIndex)", - ImmutableMap.of("s", string, "beginIndex", beginIndex))); + CelEvaluationException.class, () -> eval("s.substring(beginIndex)", variables)); String exceptionMessage = String.format( @@ -501,13 +481,12 @@ public void substring_beginIndexOutOfRange_unicode_throwsException( @TestParameters("{string: '😁😑😦', beginIndex: 2, endIndex: 1}") public void substring_beginAndEndIndexOutOfRange_throwsException( String string, int beginIndex, int endIndex) throws Exception { + ImmutableMap variables = + ImmutableMap.of("s", string, "beginIndex", beginIndex, "endIndex", endIndex); CelEvaluationException exception = assertThrows( CelEvaluationException.class, - () -> - eval( - "s.substring(beginIndex, endIndex)", - ImmutableMap.of("s", string, "beginIndex", beginIndex, "endIndex", endIndex))); + () -> eval("s.substring(beginIndex, endIndex)", variables)); String exceptionMessage = String.format("substring failure: Range [%d, %d) out of bounds", beginIndex, endIndex); @@ -516,13 +495,11 @@ public void substring_beginAndEndIndexOutOfRange_throwsException( @Test public void substring_beginIndexOverflow_throwsException() throws Exception { + ImmutableMap variables = + ImmutableMap.of("beginIndex", 2147483648L); // INT_MAX + 1 CelEvaluationException exception = assertThrows( - CelEvaluationException.class, - () -> - eval( - "'abcd'.substring(beginIndex)", - ImmutableMap.of("beginIndex", 2147483648L))); // INT_MAX + 1 + CelEvaluationException.class, () -> eval("'abcd'.substring(beginIndex)", variables)); assertThat(exception) .hasMessageThat() @@ -1381,10 +1358,7 @@ public void stringExtension_functionSubset_success() throws Exception { .build(); Object evaluatedResult = - eval( - customCel, - "'test'.substring(2) == 'st' && 'hello'.charAt(1) == 'e'", - ImmutableMap.of()); + eval(customCel, "'test'.substring(2) == 'st' && 'hello'.charAt(1) == 'e'"); assertThat(evaluatedResult).isEqualTo(true); } @@ -1499,21 +1473,5 @@ public void stringExtension_evaluateUnallowedFunction_throws() throws Exception assertThrows(CelEvaluationException.class, () -> customRuntimeCel.createProgram(ast).eval()); } - private Object eval(Cel cel, String expression, Map variables) throws Exception { - CelAbstractSyntaxTree ast; - if (isParseOnly) { - ast = cel.parse(expression).getAst(); - } else { - ast = cel.compile(expression).getAst(); - } - return cel.createProgram(ast).eval(variables); - } - - private Object eval(String expression) throws Exception { - return eval(this.cel, expression, ImmutableMap.of()); - } - private Object eval(String expression, Map variables) throws Exception { - return eval(this.cel, expression, variables); - } } 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 c017911f9..8a8786ce8 100644 --- a/optimizer/src/main/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizer.java +++ b/optimizer/src/main/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizer.java @@ -46,7 +46,6 @@ import dev.cel.optimizer.AstMutator; import dev.cel.optimizer.CelAstOptimizer; import dev.cel.optimizer.CelOptimizationException; -import dev.cel.runtime.CelAttribute.Qualifier; import dev.cel.runtime.CelAttributePattern; import dev.cel.runtime.CelEvaluationException; import dev.cel.runtime.PartialVars; @@ -683,8 +682,7 @@ private static Object evaluateExpr(Cel cel, CelNavigableMutableExpr navigableMut .allNodes() .filter(node -> node.getKind().equals(Kind.IDENT)) .map(node -> node.expr().ident().name()) - .filter(Qualifier::isLegalIdentifier) - .map(CelAttributePattern::create) + .map(CelAttributePattern::fromQualifiedIdentifier) .collect(toImmutableList()); CelAbstractSyntaxTree ast = CelAbstractSyntaxTree.newParsedAst( diff --git a/policy/src/test/java/dev/cel/policy/BUILD.bazel b/policy/src/test/java/dev/cel/policy/BUILD.bazel index 9106caf70..8a28caee1 100644 --- a/policy/src/test/java/dev/cel/policy/BUILD.bazel +++ b/policy/src/test/java/dev/cel/policy/BUILD.bazel @@ -35,7 +35,7 @@ java_library( "//policy:validation_exception", "//runtime", "//runtime:function_binding", - "//runtime:late_function_binding", + "//testing:cel_runtime_flavor", "//testing/protos:single_file_java_proto", "@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto", "@maven//:com_google_guava_guava", diff --git a/policy/src/test/java/dev/cel/policy/CelPolicyCompilerImplTest.java b/policy/src/test/java/dev/cel/policy/CelPolicyCompilerImplTest.java index fec5f9b94..d5254571d 100644 --- a/policy/src/test/java/dev/cel/policy/CelPolicyCompilerImplTest.java +++ b/policy/src/test/java/dev/cel/policy/CelPolicyCompilerImplTest.java @@ -26,9 +26,9 @@ import com.google.testing.junit.testparameterinjector.TestParameterValue; import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider; import dev.cel.bundle.Cel; +import dev.cel.bundle.CelBuilder; import dev.cel.bundle.CelEnvironment; import dev.cel.bundle.CelEnvironmentYamlParser; -import dev.cel.bundle.CelFactory; import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelOptions; import dev.cel.common.types.OptionalType; @@ -45,6 +45,7 @@ import dev.cel.policy.PolicyTestHelper.TestYamlPolicy; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.CelLateFunctionBindings; +import dev.cel.testing.CelRuntimeFlavor; import dev.cel.testing.testdata.SingleFile; import dev.cel.testing.testdata.proto3.StandaloneGlobalEnum; import java.io.IOException; @@ -61,7 +62,12 @@ public final class CelPolicyCompilerImplTest { private static final CelEnvironmentYamlParser ENVIRONMENT_PARSER = CelEnvironmentYamlParser.newInstance(); private static final CelOptions CEL_OPTIONS = - CelOptions.current().populateMacroCalls(true).build(); + CelOptions.current() + .populateMacroCalls(true) + .enableHeterogeneousNumericComparisons(true) + .build(); + + @TestParameter public CelRuntimeFlavor runtimeFlavor; @Test public void compileYamlPolicy_success(@TestParameter TestYamlPolicy yamlPolicy) throws Exception { @@ -258,7 +264,6 @@ public void evaluateYamlPolicy_nestedRuleProducesOptionalOutput() throws Excepti CelPolicy policy = POLICY_PARSER.parse(policySource); CelAbstractSyntaxTree compiledPolicyAst = CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy); - Optional evalResult = (Optional) cel.createProgram(compiledPolicyAst).eval(); // Result is Optional> @@ -278,7 +283,12 @@ public void evaluateYamlPolicy_lateBoundFunction() throws Exception { + " return:\n" + " type_name: 'string'\n"; CelEnvironment celEnvironment = ENVIRONMENT_PARSER.parse(configSource); - Cel cel = celEnvironment.extend(newCel(), CelOptions.DEFAULT); + CelBuilder celBuilder = newCel().toCelBuilder(); + if (runtimeFlavor == CelRuntimeFlavor.PLANNER) { + celBuilder.addLateBoundFunctions("lateBoundFunc"); + } + Cel cel = celEnvironment.extend(celBuilder.build(), CEL_OPTIONS); + String policySource = "name: late_bound_function_policy\n" + "rule:\n" @@ -298,7 +308,6 @@ public void evaluateYamlPolicy_lateBoundFunction() throws Exception { (String) cel.createProgram(compiledPolicyAst) .eval((unused) -> Optional.empty(), lateFunctionBindings); - assertThat(evalResult).isEqualTo("foo" + exampleValue); } @@ -319,7 +328,6 @@ public void evaluateYamlPolicy_withSimpleVariable() throws Exception { CelAbstractSyntaxTree compiledPolicyAst = CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy); - boolean evalResult = (boolean) cel.createProgram(compiledPolicyAst).eval(); assertThat(evalResult).isFalse(); @@ -358,8 +366,9 @@ protected ImmutableList provideValues(Context context) throw } } - private static Cel newCel() { - return CelFactory.standardCelBuilder() + private Cel newCel() { + return runtimeFlavor + .builder() .setStandardMacros(CelStandardMacro.STANDARD_MACROS) .addCompilerLibraries(CelOptionalLibrary.INSTANCE) .addRuntimeLibraries(CelOptionalLibrary.INSTANCE) @@ -367,19 +376,21 @@ private static Cel newCel() { .addMessageTypes(TestAllTypes.getDescriptor(), SingleFile.getDescriptor()) .setOptions(CEL_OPTIONS) .addFunctionBindings( - CelFunctionBinding.from( - "locationCode_string", - String.class, - (ip) -> { - switch (ip) { - case "10.0.0.1": - return "us"; - case "10.0.0.2": - return "de"; - default: - return "ir"; - } - })) + CelFunctionBinding.fromOverloads( + "locationCode", + CelFunctionBinding.from( + "locationCode_string", + String.class, + (ip) -> { + switch (ip) { + case "10.0.0.1": + return "us"; + case "10.0.0.2": + return "de"; + default: + return "ir"; + } + }))) .build(); } diff --git a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel index 6f0607de4..ef0ac71d4 100644 --- a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel @@ -53,6 +53,7 @@ LITE_PROGRAM_IMPL_SOURCES = [ FUNCTION_BINDING_SOURCES = [ "CelFunctionBinding.java", "FunctionBindingImpl.java", + "InternalCelFunctionBinding.java", ] # keep sorted @@ -740,6 +741,7 @@ java_library( deps = [ ":evaluation_exception", ":function_overload", + "//common/annotations", "//common/exceptions:overload_not_found", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", @@ -754,6 +756,7 @@ cel_android_library( deps = [ ":evaluation_exception", ":function_overload_android", + "//common/annotations", "//common/exceptions:overload_not_found", "@maven//:com_google_errorprone_error_prone_annotations", "@maven_android//:com_google_guava_guava", @@ -890,7 +893,6 @@ java_library( "//common/types:type_providers", "//common/values:cel_value_provider", "//common/values:proto_message_value_provider", - "//runtime/standard:add", "//runtime/standard:int", "//runtime/standard:timestamp", "@maven//:com_google_code_findbugs_annotations", diff --git a/runtime/src/main/java/dev/cel/runtime/CelFunctionBinding.java b/runtime/src/main/java/dev/cel/runtime/CelFunctionBinding.java index 88be0d3c3..98991d383 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelFunctionBinding.java +++ b/runtime/src/main/java/dev/cel/runtime/CelFunctionBinding.java @@ -100,6 +100,7 @@ static CelFunctionBinding from( overloadId, ImmutableList.copyOf(argTypes), impl, /* isStrict= */ true); } + /** See {@link #fromOverloads(String, Collection)}. */ static ImmutableSet fromOverloads( String functionName, CelFunctionBinding... overloadBindings) { diff --git a/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java b/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java index 3d75845cf..2da08120c 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java +++ b/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java @@ -63,7 +63,12 @@ public static CelLateFunctionBindings from(Collection functi } private static CelResolvedOverload createResolvedOverload(CelFunctionBinding binding) { + String functionName = binding.getOverloadId(); + if (binding instanceof InternalCelFunctionBinding) { + functionName = ((InternalCelFunctionBinding) binding).getFunctionName(); + } return CelResolvedOverload.of( + functionName, binding.getOverloadId(), binding.getDefinition(), binding.isStrict(), diff --git a/runtime/src/main/java/dev/cel/runtime/CelResolvedOverload.java b/runtime/src/main/java/dev/cel/runtime/CelResolvedOverload.java index 7063720a1..fbe9a3289 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelResolvedOverload.java +++ b/runtime/src/main/java/dev/cel/runtime/CelResolvedOverload.java @@ -30,6 +30,9 @@ @Internal public abstract class CelResolvedOverload { + /** The base function name. */ + public abstract String getFunctionName(); + /** The overload id of the function. */ public abstract String getOverloadId(); @@ -61,7 +64,7 @@ public Object invoke(Object[] args) throws CelEvaluationException { || CelFunctionOverload.canHandle(args, getParameterTypes(), isStrict())) { return getDefinition().apply(args); } - throw new CelOverloadNotFoundException(getOverloadId()); + throw new CelOverloadNotFoundException(getFunctionName(), ImmutableList.of(getOverloadId())); } public Object invoke(Object arg) throws CelEvaluationException { @@ -69,7 +72,7 @@ public Object invoke(Object arg) throws CelEvaluationException { || CelFunctionOverload.canHandle(arg, getParameterTypes(), isStrict())) { return getOptimizedDefinition().apply(arg); } - throw new CelOverloadNotFoundException(getOverloadId()); + throw new CelOverloadNotFoundException(getFunctionName(), ImmutableList.of(getOverloadId())); } public Object invoke(Object arg1, Object arg2) throws CelEvaluationException { @@ -77,24 +80,28 @@ public Object invoke(Object arg1, Object arg2) throws CelEvaluationException { || CelFunctionOverload.canHandle(arg1, arg2, getParameterTypes(), isStrict())) { return getOptimizedDefinition().apply(arg1, arg2); } - throw new CelOverloadNotFoundException(getOverloadId()); + throw new CelOverloadNotFoundException(getFunctionName(), ImmutableList.of(getOverloadId())); } /** - * Creates a new resolved overload from the given overload id, parameter types, and definition. + * Creates a new resolved overload from the given function name, overload id, parameter types, and + * definition. */ public static CelResolvedOverload of( + String functionName, String overloadId, CelFunctionOverload definition, boolean isStrict, Class... parameterTypes) { - return of(overloadId, definition, isStrict, ImmutableList.copyOf(parameterTypes)); + return of(functionName, overloadId, definition, isStrict, ImmutableList.copyOf(parameterTypes)); } /** - * Creates a new resolved overload from the given overload id, parameter types, and definition. + * Creates a new resolved overload from the given function name, overload id, parameter types, and + * definition. */ public static CelResolvedOverload of( + String functionName, String overloadId, CelFunctionOverload definition, boolean isStrict, @@ -104,7 +111,12 @@ public static CelResolvedOverload of( ? (OptimizedFunctionOverload) definition : definition::apply; return new AutoValue_CelResolvedOverload( - overloadId, ImmutableList.copyOf(parameterTypes), isStrict, definition, optimizedDef); + functionName, + overloadId, + ImmutableList.copyOf(parameterTypes), + isStrict, + definition, + optimizedDef); } /** diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java b/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java index cab2c666e..43b223fa0 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java +++ b/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java @@ -381,7 +381,12 @@ private static DefaultDispatcher newDispatcher( DefaultDispatcher.Builder builder = DefaultDispatcher.newBuilder(); for (CelFunctionBinding binding : standardFunctions.newFunctionBindings(runtimeEquality, options)) { + String functionName = binding.getOverloadId(); + if (binding instanceof InternalCelFunctionBinding) { + functionName = ((InternalCelFunctionBinding) binding).getFunctionName(); + } builder.addOverload( + functionName, binding.getOverloadId(), binding.getArgTypes(), binding.isStrict(), @@ -389,7 +394,12 @@ private static DefaultDispatcher newDispatcher( } for (CelFunctionBinding binding : customFunctionBindings) { + String functionName = binding.getOverloadId(); + if (binding instanceof InternalCelFunctionBinding) { + functionName = ((InternalCelFunctionBinding) binding).getFunctionName(); + } builder.addOverload( + functionName, binding.getOverloadId(), binding.getArgTypes(), binding.isStrict(), diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java b/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java index 8ae4a9e3e..33702b2c6 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java +++ b/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java @@ -305,7 +305,12 @@ public CelRuntimeLegacyImpl build() { DefaultDispatcher.Builder dispatcherBuilder = DefaultDispatcher.newBuilder(); for (CelFunctionBinding standardFunctionBinding : newStandardFunctionBindings(runtimeEquality)) { + String functionName = standardFunctionBinding.getOverloadId(); + if (standardFunctionBinding instanceof InternalCelFunctionBinding) { + functionName = ((InternalCelFunctionBinding) standardFunctionBinding).getFunctionName(); + } dispatcherBuilder.addOverload( + functionName, standardFunctionBinding.getOverloadId(), standardFunctionBinding.getArgTypes(), standardFunctionBinding.isStrict(), @@ -313,7 +318,12 @@ public CelRuntimeLegacyImpl build() { } for (CelFunctionBinding customBinding : customFunctionBindings.values()) { + String functionName = customBinding.getOverloadId(); + if (customBinding instanceof InternalCelFunctionBinding) { + functionName = ((InternalCelFunctionBinding) customBinding).getFunctionName(); + } dispatcherBuilder.addOverload( + functionName, customBinding.getOverloadId(), customBinding.getArgTypes(), customBinding.isStrict(), diff --git a/runtime/src/main/java/dev/cel/runtime/DefaultDispatcher.java b/runtime/src/main/java/dev/cel/runtime/DefaultDispatcher.java index d6ddf3965..0a467db81 100644 --- a/runtime/src/main/java/dev/cel/runtime/DefaultDispatcher.java +++ b/runtime/src/main/java/dev/cel/runtime/DefaultDispatcher.java @@ -134,6 +134,8 @@ public static class Builder { @AutoValue @Immutable abstract static class OverloadEntry { + abstract String functionName(); + abstract ImmutableList> argTypes(); abstract boolean isStrict(); @@ -141,8 +143,12 @@ abstract static class OverloadEntry { abstract CelFunctionOverload overload(); private static OverloadEntry of( - ImmutableList> argTypes, boolean isStrict, CelFunctionOverload overload) { - return new AutoValue_DefaultDispatcher_Builder_OverloadEntry(argTypes, isStrict, overload); + String functionName, + ImmutableList> argTypes, + boolean isStrict, + CelFunctionOverload overload) { + return new AutoValue_DefaultDispatcher_Builder_OverloadEntry( + functionName, argTypes, isStrict, overload); } } @@ -150,16 +156,19 @@ private static OverloadEntry of( @CanIgnoreReturnValue public Builder addOverload( + String functionName, String overloadId, ImmutableList> argTypes, boolean isStrict, CelFunctionOverload overload) { + checkNotNull(functionName); + checkArgument(!functionName.isEmpty(), "Function name cannot be empty."); checkNotNull(overloadId); checkArgument(!overloadId.isEmpty(), "Overload ID cannot be empty."); checkNotNull(argTypes); checkNotNull(overload); - OverloadEntry newEntry = OverloadEntry.of(argTypes, isStrict, overload); + OverloadEntry newEntry = OverloadEntry.of(functionName, argTypes, isStrict, overload); overloads.merge( overloadId, @@ -188,7 +197,7 @@ private OverloadEntry mergeDynamicDispatchesOrThrow( boolean isStrict = mergedOverload.getOverloadBindings().stream().allMatch(CelFunctionBinding::isStrict); - return OverloadEntry.of(incoming.argTypes(), isStrict, mergedOverload); + return OverloadEntry.of(overloadId, incoming.argTypes(), isStrict, mergedOverload); } throw new IllegalArgumentException("Duplicate overload ID binding: " + overloadId); @@ -204,7 +213,11 @@ public DefaultDispatcher build() { resolvedOverloads.put( overloadId, CelResolvedOverload.of( - overloadId, overloadImpl, overloadEntry.isStrict(), overloadEntry.argTypes())); + overloadEntry.functionName(), + overloadId, + overloadImpl, + overloadEntry.isStrict(), + overloadEntry.argTypes())); } return new DefaultDispatcher(resolvedOverloads.buildOrThrow()); diff --git a/runtime/src/main/java/dev/cel/runtime/FunctionBindingImpl.java b/runtime/src/main/java/dev/cel/runtime/FunctionBindingImpl.java index c1306ce19..7b8efe8fd 100644 --- a/runtime/src/main/java/dev/cel/runtime/FunctionBindingImpl.java +++ b/runtime/src/main/java/dev/cel/runtime/FunctionBindingImpl.java @@ -23,7 +23,9 @@ import dev.cel.common.exceptions.CelOverloadNotFoundException; @Immutable -final class FunctionBindingImpl implements CelFunctionBinding { +final class FunctionBindingImpl implements InternalCelFunctionBinding { + + private final String functionName; private final String overloadId; @@ -33,6 +35,11 @@ final class FunctionBindingImpl implements CelFunctionBinding { private final boolean isStrict; + @Override + public String getFunctionName() { + return functionName; + } + @Override public String getOverloadId() { return overloadId; @@ -54,20 +61,34 @@ public boolean isStrict() { } FunctionBindingImpl( + String functionName, String overloadId, ImmutableList> argTypes, CelFunctionOverload definition, boolean isStrict) { + this.functionName = functionName; this.overloadId = overloadId; this.argTypes = argTypes; this.definition = definition; this.isStrict = isStrict; } + FunctionBindingImpl( + String overloadId, + ImmutableList> argTypes, + CelFunctionOverload definition, + boolean isStrict) { + this(overloadId, overloadId, argTypes, definition, isStrict); + } + static ImmutableSet groupOverloadsToFunction( String functionName, ImmutableSet overloadBindings) { ImmutableSet.Builder builder = ImmutableSet.builder(); - builder.addAll(overloadBindings); + for (CelFunctionBinding b : overloadBindings) { + builder.add( + new FunctionBindingImpl( + functionName, b.getOverloadId(), b.getArgTypes(), b.getDefinition(), b.isStrict())); + } // If there is already a binding with the same name as the function, we treat it as a // "Singleton" binding and do not create a dynamic dispatch wrapper for it. @@ -80,11 +101,12 @@ static ImmutableSet groupOverloadsToFunction( CelFunctionBinding singleBinding = Iterables.getOnlyElement(overloadBindings); builder.add( new FunctionBindingImpl( + functionName, functionName, singleBinding.getArgTypes(), singleBinding.getDefinition(), singleBinding.isStrict())); - } else { + } else if (overloadBindings.size() > 1) { builder.add(new DynamicDispatchBinding(functionName, overloadBindings)); } } @@ -93,7 +115,7 @@ static ImmutableSet groupOverloadsToFunction( } @Immutable - static final class DynamicDispatchBinding implements CelFunctionBinding { + static final class DynamicDispatchBinding implements InternalCelFunctionBinding { private final boolean isStrict; private final DynamicDispatchOverload dynamicDispatchOverload; @@ -103,6 +125,11 @@ public String getOverloadId() { return dynamicDispatchOverload.functionName; } + @Override + public String getFunctionName() { + return dynamicDispatchOverload.functionName; + } + @Override public ImmutableList> getArgTypes() { return ImmutableList.of(); diff --git a/runtime/src/main/java/dev/cel/runtime/InternalCelFunctionBinding.java b/runtime/src/main/java/dev/cel/runtime/InternalCelFunctionBinding.java new file mode 100644 index 000000000..48a0f36d1 --- /dev/null +++ b/runtime/src/main/java/dev/cel/runtime/InternalCelFunctionBinding.java @@ -0,0 +1,29 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev.cel.runtime; + +import com.google.errorprone.annotations.Immutable; +import dev.cel.common.annotations.Internal; + +/** + * Internal interface to expose the function name associated with a binding. + * + *

CEL Library Internals. Do Not Use. + */ +@Internal +@Immutable +public interface InternalCelFunctionBinding extends CelFunctionBinding { + String getFunctionName(); +} diff --git a/runtime/src/main/java/dev/cel/runtime/LiteRuntimeImpl.java b/runtime/src/main/java/dev/cel/runtime/LiteRuntimeImpl.java index 0e5c5cf30..d58eb3be4 100644 --- a/runtime/src/main/java/dev/cel/runtime/LiteRuntimeImpl.java +++ b/runtime/src/main/java/dev/cel/runtime/LiteRuntimeImpl.java @@ -162,9 +162,18 @@ public CelLiteRuntime build() { functionBindingsBuilder .buildOrThrow() .forEach( - (String overloadId, CelFunctionBinding func) -> - dispatcherBuilder.addOverload( - overloadId, func.getArgTypes(), func.isStrict(), func.getDefinition())); + (String overloadId, CelFunctionBinding func) -> { + String functionName = func.getOverloadId(); + if (func instanceof InternalCelFunctionBinding) { + functionName = ((InternalCelFunctionBinding) func).getFunctionName(); + } + dispatcherBuilder.addOverload( + functionName, + overloadId, + func.getArgTypes(), + func.isStrict(), + func.getDefinition()); + }); Interpreter interpreter = new DefaultInterpreter( diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalBinary.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalBinary.java index 7771da3e6..16eba3cce 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalBinary.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalBinary.java @@ -25,6 +25,7 @@ final class EvalBinary extends PlannedInterpretable { + private final String functionName; private final CelResolvedOverload resolvedOverload; private final PlannedInterpretable arg1; private final PlannedInterpretable arg2; @@ -48,25 +49,29 @@ public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEval return unknowns; } - return EvalHelpers.dispatch(resolvedOverload, celValueConverter, argVal1, argVal2); + return EvalHelpers.dispatch( + functionName, resolvedOverload, celValueConverter, argVal1, argVal2); } static EvalBinary create( long exprId, + String functionName, CelResolvedOverload resolvedOverload, PlannedInterpretable arg1, PlannedInterpretable arg2, CelValueConverter celValueConverter) { - return new EvalBinary(exprId, resolvedOverload, arg1, arg2, celValueConverter); + return new EvalBinary(exprId, functionName, resolvedOverload, arg1, arg2, celValueConverter); } private EvalBinary( long exprId, + String functionName, CelResolvedOverload resolvedOverload, PlannedInterpretable arg1, PlannedInterpretable arg2, CelValueConverter celValueConverter) { super(exprId); + this.functionName = functionName; this.resolvedOverload = resolvedOverload; this.arg1 = arg1; this.arg2 = arg2; diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java index a30f91880..220642f4a 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java @@ -56,7 +56,10 @@ static Object evalStrictly( } static Object dispatch( - CelResolvedOverload overload, CelValueConverter valueConverter, Object[] args) + String functionName, + CelResolvedOverload overload, + CelValueConverter valueConverter, + Object[] args) throws CelEvaluationException { try { Object result = overload.invoke(args); @@ -66,7 +69,11 @@ static Object dispatch( } } - static Object dispatch(CelResolvedOverload overload, CelValueConverter valueConverter, Object arg) + static Object dispatch( + String functionName, + CelResolvedOverload overload, + CelValueConverter valueConverter, + Object arg) throws CelEvaluationException { try { Object result = overload.invoke(arg); @@ -77,7 +84,11 @@ static Object dispatch(CelResolvedOverload overload, CelValueConverter valueConv } static Object dispatch( - CelResolvedOverload overload, CelValueConverter valueConverter, Object arg1, Object arg2) + String functionName, + CelResolvedOverload overload, + CelValueConverter valueConverter, + Object arg1, + Object arg2) throws CelEvaluationException { try { Object result = overload.invoke(arg1, arg2); @@ -97,7 +108,7 @@ private static RuntimeException handleDispatchException( return new IllegalArgumentException( String.format( "Function '%s' failed with arg(s) '%s'", - overload.getOverloadId(), Joiner.on(", ").join(args)), + overload.getFunctionName(), Joiner.on(", ").join(args)), e); } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalLateBoundCall.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalLateBoundCall.java index cdee878ee..0bd251185 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalLateBoundCall.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalLateBoundCall.java @@ -55,7 +55,7 @@ public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEval .findOverload(functionName, overloadIds, argVals) .orElseThrow(() -> new CelOverloadNotFoundException(functionName, overloadIds)); - return EvalHelpers.dispatch(resolvedOverload, celValueConverter, argVals); + return EvalHelpers.dispatch(functionName, resolvedOverload, celValueConverter, argVals); } static EvalLateBoundCall create( diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java index 322648ee3..57834161f 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java @@ -24,6 +24,7 @@ final class EvalUnary extends PlannedInterpretable { + private final String functionName; private final CelResolvedOverload resolvedOverload; private final PlannedInterpretable arg; private final CelValueConverter celValueConverter; @@ -34,23 +35,26 @@ public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEval resolvedOverload.isStrict() ? evalStrictly(arg, resolver, frame) : evalNonstrictly(arg, resolver, frame); - return EvalHelpers.dispatch(resolvedOverload, celValueConverter, argVal); + return EvalHelpers.dispatch(functionName, resolvedOverload, celValueConverter, argVal); } static EvalUnary create( long exprId, + String functionName, CelResolvedOverload resolvedOverload, PlannedInterpretable arg, CelValueConverter celValueConverter) { - return new EvalUnary(exprId, resolvedOverload, arg, celValueConverter); + return new EvalUnary(exprId, functionName, resolvedOverload, arg, celValueConverter); } private EvalUnary( long exprId, + String functionName, CelResolvedOverload resolvedOverload, PlannedInterpretable arg, CelValueConverter celValueConverter) { super(exprId); + this.functionName = functionName; this.resolvedOverload = resolvedOverload; this.arg = arg; this.celValueConverter = celValueConverter; diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java index eb8745632..fe7c6c430 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java @@ -25,6 +25,7 @@ final class EvalVarArgsCall extends PlannedInterpretable { + private final String functionName; private final CelResolvedOverload resolvedOverload; @SuppressWarnings("Immutable") @@ -50,23 +51,26 @@ public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEval return unknowns; } - return EvalHelpers.dispatch(resolvedOverload, celValueConverter, argVals); + return EvalHelpers.dispatch(functionName, resolvedOverload, celValueConverter, argVals); } static EvalVarArgsCall create( long exprId, + String functionName, CelResolvedOverload resolvedOverload, PlannedInterpretable[] args, CelValueConverter celValueConverter) { - return new EvalVarArgsCall(exprId, resolvedOverload, args, celValueConverter); + return new EvalVarArgsCall(exprId, functionName, resolvedOverload, args, celValueConverter); } private EvalVarArgsCall( long exprId, + String functionName, CelResolvedOverload resolvedOverload, PlannedInterpretable[] args, CelValueConverter celValueConverter) { super(exprId); + this.functionName = functionName; this.resolvedOverload = resolvedOverload; this.args = args; this.celValueConverter = celValueConverter; diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java index 5b3138207..7798c8253 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java @@ -22,22 +22,30 @@ final class EvalZeroArity extends PlannedInterpretable { private static final Object[] EMPTY_ARRAY = new Object[0]; + private final String functionName; private final CelResolvedOverload resolvedOverload; private final CelValueConverter celValueConverter; @Override public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEvaluationException { - return EvalHelpers.dispatch(resolvedOverload, celValueConverter, EMPTY_ARRAY); + return EvalHelpers.dispatch(functionName, resolvedOverload, celValueConverter, EMPTY_ARRAY); } static EvalZeroArity create( - long exprId, CelResolvedOverload resolvedOverload, CelValueConverter celValueConverter) { - return new EvalZeroArity(exprId, resolvedOverload, celValueConverter); + long exprId, + String functionName, + CelResolvedOverload resolvedOverload, + CelValueConverter celValueConverter) { + return new EvalZeroArity(exprId, functionName, resolvedOverload, celValueConverter); } private EvalZeroArity( - long exprId, CelResolvedOverload resolvedOverload, CelValueConverter celValueConverter) { + long exprId, + String functionName, + CelResolvedOverload resolvedOverload, + CelValueConverter celValueConverter) { super(exprId); + this.functionName = functionName; this.resolvedOverload = resolvedOverload; this.celValueConverter = celValueConverter; } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java b/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java index 9bd5f3ecd..a0b74fc99 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java @@ -308,15 +308,21 @@ private PlannedInterpretable planCall(CelExpr expr, PlannerContext ctx) { switch (argCount) { case 0: - return EvalZeroArity.create(expr.id(), resolvedOverload, celValueConverter); + return EvalZeroArity.create(expr.id(), functionName, resolvedOverload, celValueConverter); case 1: - return EvalUnary.create(expr.id(), resolvedOverload, evaluatedArgs[0], celValueConverter); + return EvalUnary.create( + expr.id(), functionName, resolvedOverload, evaluatedArgs[0], celValueConverter); case 2: return EvalBinary.create( - expr.id(), resolvedOverload, evaluatedArgs[0], evaluatedArgs[1], celValueConverter); + expr.id(), + functionName, + resolvedOverload, + evaluatedArgs[0], + evaluatedArgs[1], + celValueConverter); default: return EvalVarArgsCall.create( - expr.id(), resolvedOverload, evaluatedArgs, celValueConverter); + expr.id(), functionName, resolvedOverload, evaluatedArgs, celValueConverter); } } diff --git a/runtime/src/test/java/dev/cel/runtime/BUILD.bazel b/runtime/src/test/java/dev/cel/runtime/BUILD.bazel index 577010971..7cd24f040 100644 --- a/runtime/src/test/java/dev/cel/runtime/BUILD.bazel +++ b/runtime/src/test/java/dev/cel/runtime/BUILD.bazel @@ -2,6 +2,7 @@ load("@rules_java//java:defs.bzl", "java_library") load("//:cel_android_rules.bzl", "cel_android_local_test") load("//:testing.bzl", "junit4_test_suites") +# Invalidate cache after file removal package( default_applicable_licenses = ["//:license"], default_testonly = True, diff --git a/runtime/src/test/java/dev/cel/runtime/CelResolvedOverloadTest.java b/runtime/src/test/java/dev/cel/runtime/CelResolvedOverloadTest.java index c1210c1ba..471282117 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelResolvedOverloadTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelResolvedOverloadTest.java @@ -27,11 +27,13 @@ public final class CelResolvedOverloadTest { CelResolvedOverload getIncrementIntOverload() { return CelResolvedOverload.of( - "increment_int", - (args) -> { - Long arg = (Long) args[0]; - return arg + 1; - }, + /* functionName= */ "increment_int", + /* overloadId= */ "increment_int_overload", + (CelFunctionOverload) + (args) -> { + Long arg = (Long) args[0]; + return arg + 1; + }, /* isStrict= */ true, Long.class); } @@ -45,14 +47,23 @@ public void canHandle_matchingTypes_returnsTrue() { public void canHandle_nullMessageType_returnsFalse() { CelResolvedOverload overload = CelResolvedOverload.of( - "identity", (args) -> args[0], /* isStrict= */ true, TestAllTypes.class); + /* functionName= */ "identity", + /* overloadId= */ "identity_overload", + (CelFunctionOverload) (args) -> args[0], + /* isStrict= */ true, + TestAllTypes.class); assertThat(overload.canHandle(new Object[] {null})).isFalse(); } @Test public void canHandle_nullPrimitive_returnsFalse() { CelResolvedOverload overload = - CelResolvedOverload.of("identity", (args) -> args[0], /* isStrict= */ true, Long.class); + CelResolvedOverload.of( + /* functionName= */ "identity", + /* overloadId= */ "identity_overload", + (CelFunctionOverload) (args) -> args[0], + /* isStrict= */ true, + Long.class); assertThat(overload.canHandle(new Object[] {null})).isFalse(); } @@ -70,10 +81,12 @@ public void canHandle_nonMatchingArgCount_returnsFalse() { public void canHandle_nonStrictOverload_returnsTrue() { CelResolvedOverload nonStrictOverload = CelResolvedOverload.of( - "non_strict", - (args) -> { - return false; - }, + /* functionName= */ "non_strict", + /* overloadId= */ "non_strict_overload", + (CelFunctionOverload) + (args) -> { + return false; + }, /* isStrict= */ false, Long.class, Long.class); @@ -87,10 +100,12 @@ public void canHandle_nonStrictOverload_returnsTrue() { public void canHandle_nonStrictOverload_returnsFalse() { CelResolvedOverload nonStrictOverload = CelResolvedOverload.of( - "non_strict", - (args) -> { - return false; - }, + /* functionName= */ "non_strict", + /* overloadId= */ "non_strict_overload", + (CelFunctionOverload) + (args) -> { + return false; + }, /* isStrict= */ false, Long.class, Long.class); diff --git a/runtime/src/test/java/dev/cel/runtime/DefaultDispatcherTest.java b/runtime/src/test/java/dev/cel/runtime/DefaultDispatcherTest.java index 255360ee1..d862ddb33 100644 --- a/runtime/src/test/java/dev/cel/runtime/DefaultDispatcherTest.java +++ b/runtime/src/test/java/dev/cel/runtime/DefaultDispatcherTest.java @@ -37,11 +37,19 @@ public void setup() { overloads.put( "overload_1", CelResolvedOverload.of( - "overload_1", args -> (Long) args[0] + 1, /* isStrict= */ true, Long.class)); + /* functionName= */ "overload_1", + /* overloadId= */ "overload_1", + args -> (Long) args[0] + 1, + /* isStrict= */ true, + Long.class)); overloads.put( "overload_2", CelResolvedOverload.of( - "overload_2", args -> (Long) args[0] + 2, /* isStrict= */ true, Long.class)); + /* functionName= */ "overload_2", + /* overloadId= */ "overload_2", + args -> (Long) args[0] + 2, + /* isStrict= */ true, + Long.class)); } @Test diff --git a/runtime/src/test/java/dev/cel/runtime/DefaultInterpreterTest.java b/runtime/src/test/java/dev/cel/runtime/DefaultInterpreterTest.java index 1a8f45161..bd0e96856 100644 --- a/runtime/src/test/java/dev/cel/runtime/DefaultInterpreterTest.java +++ b/runtime/src/test/java/dev/cel/runtime/DefaultInterpreterTest.java @@ -77,15 +77,21 @@ public Object adapt(String messageName, Object message) { CelAbstractSyntaxTree ast = celCompiler.compile("[1].all(x, [2].all(y, error()))").getAst(); DefaultDispatcher.Builder dispatcherBuilder = DefaultDispatcher.newBuilder(); dispatcherBuilder.addOverload( - "error", - ImmutableList.of(long.class), + /* functionName= */ "error", + /* overloadId= */ "error_overload", + ImmutableList.>of(long.class), /* isStrict= */ true, (args) -> new IllegalArgumentException("Always throws")); CelFunctionBinding notStrictlyFalseBinding = NotStrictlyFalseOverload.NOT_STRICTLY_FALSE.newFunctionBinding( CelOptions.DEFAULT, RuntimeEquality.create(RuntimeHelpers.create(), CelOptions.DEFAULT)); + String functionName = notStrictlyFalseBinding.getOverloadId(); + if (notStrictlyFalseBinding instanceof InternalCelFunctionBinding) { + functionName = ((InternalCelFunctionBinding) notStrictlyFalseBinding).getFunctionName(); + } dispatcherBuilder.addOverload( + functionName, notStrictlyFalseBinding.getOverloadId(), notStrictlyFalseBinding.getArgTypes(), notStrictlyFalseBinding.isStrict(), diff --git a/runtime/src/test/java/dev/cel/runtime/PlannerInterpreterTest.java b/runtime/src/test/java/dev/cel/runtime/PlannerInterpreterTest.java index 2b0e53298..c0b0f76c4 100644 --- a/runtime/src/test/java/dev/cel/runtime/PlannerInterpreterTest.java +++ b/runtime/src/test/java/dev/cel/runtime/PlannerInterpreterTest.java @@ -82,13 +82,14 @@ protected CelAbstractSyntaxTree prepareTest(CelTypeProvider typeProvider) { @Override public void optional_errors() { - if (isParseOnly) { - // Parsed-only evaluation contains function name in the - // error message instead of the function overload. - skipBaselineVerification(); - } else { - super.optional_errors(); - } + // Exercised in planner_optional_errors instead + skipBaselineVerification(); + } + + @Test + public void planner_optional_errors() { + source = "optional.unwrap([dyn(1)])"; + runTest(ImmutableMap.of()); } @Override diff --git a/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java b/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java index de30902d3..c58ae782b 100644 --- a/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java +++ b/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java @@ -75,6 +75,7 @@ import dev.cel.runtime.CelUnknownSet; import dev.cel.runtime.DefaultDispatcher; import dev.cel.runtime.DescriptorTypeResolver; +import dev.cel.runtime.InternalCelFunctionBinding; import dev.cel.runtime.PartialVars; import dev.cel.runtime.Program; import dev.cel.runtime.RuntimeEquality; @@ -244,6 +245,7 @@ private static void addBindingsToDispatcher( overloadBindings.forEach( overload -> builder.addOverload( + ((InternalCelFunctionBinding) overload).getFunctionName(), overload.getOverloadId(), overload.getArgTypes(), overload.isStrict(), @@ -494,15 +496,11 @@ public void plan_call_zeroArgs() throws Exception { public void plan_call_throws() throws Exception { CelAbstractSyntaxTree ast = compile("error()"); Program program = PLANNER.plan(ast); - String expectedOverloadId = isParseOnly ? "error" : "error_overload"; CelEvaluationException e = assertThrows(CelEvaluationException.class, program::eval); assertThat(e) .hasMessageThat() - .contains( - "evaluation error at :5: Function '" - + expectedOverloadId - + "' failed with arg(s) ''"); + .contains("evaluation error at :5: Function 'error' failed with arg(s) ''"); assertThat(e).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(e.getCause()).hasMessageThat().contains("Intentional error"); } @@ -562,13 +560,11 @@ public void plan_call_mapIndex() throws Exception { public void plan_call_noMatchingOverload_throws() throws Exception { CelAbstractSyntaxTree ast = compile("concat(b'abc', dyn_var)"); Program program = PLANNER.plan(ast); - String errorMsg; + String errorMsg = + "No matching overload for function 'concat'. Overload candidates: concat_bytes_bytes"; if (isParseOnly) { - errorMsg = - "No matching overload for function 'concat'. Overload candidates: concat_bytes_bytes," - + " bytes_concat_bytes"; - } else { - errorMsg = "No matching overload for function 'concat_bytes_bytes'"; + // Parsed-only evaluation includes both overloads as candidates due to dynamic dispatch + errorMsg += ", bytes_concat_bytes"; } CelEvaluationException e = diff --git a/runtime/src/test/resources/planner_optional_errors.baseline b/runtime/src/test/resources/planner_optional_errors.baseline new file mode 100644 index 000000000..3d59fefca --- /dev/null +++ b/runtime/src/test/resources/planner_optional_errors.baseline @@ -0,0 +1,5 @@ +Source: optional.unwrap([dyn(1)]) +=====> +bindings: {} +error: evaluation error at test_location:15: Function 'optional.unwrap' failed with arg(s) '[1]' +error_code: INTERNAL_ERROR