From c6300a25c73ad12328f5596a09294fd6ce8a05b4 Mon Sep 17 00:00:00 2001 From: Sean Huh Date: Fri, 16 Oct 2026 12:28:04 -0700 Subject: [PATCH] No public description PiperOrigin-RevId: 1001026030 --- .../common/values/ProtoMessageLiteValue.java | 18 ++- .../values/ProtoMessageLiteValueTest.java | 22 ++- .../test/java/dev/cel/conformance/BUILD.bazel | 72 +++++++++- .../dev/cel/conformance/ConformanceTest.java | 129 +++++++++++++----- .../conformance/ConformanceTestRunner.java | 16 +-- .../dev/cel/conformance/conformance_test.bzl | 20 ++- .../src/main/java/dev/cel/runtime/BUILD.bazel | 4 + .../dev/cel/runtime/CelLiteRuntimeImpl.java | 25 ++-- .../java/dev/cel/runtime/TypeResolver.java | 14 +- .../dev/cel/runtime/CelLiteRuntimeTest.java | 42 ++++-- .../java/dev/cel/testing/utils/BUILD.bazel | 1 + .../dev/cel/testing/utils/ExprValueUtils.java | 6 +- 12 files changed, 281 insertions(+), 88 deletions(-) diff --git a/common/src/main/java/dev/cel/common/values/ProtoMessageLiteValue.java b/common/src/main/java/dev/cel/common/values/ProtoMessageLiteValue.java index f864d355c..694df2b0b 100644 --- a/common/src/main/java/dev/cel/common/values/ProtoMessageLiteValue.java +++ b/common/src/main/java/dev/cel/common/values/ProtoMessageLiteValue.java @@ -133,19 +133,23 @@ public final String toString() { @Override public Object select(String field) { - FieldLiteDescriptor fd = descriptor().findByFieldName(field); - if (fd == null) { - throw CelAttributeNotFoundException.of( - String.format("field '%s' is not declared in message '%s'", field, protoTypeName())); - } + FieldLiteDescriptor fd = findFieldDescriptor(field); Object fieldValue = readField(fd); return fieldValue != null ? fieldValue : protoLiteCelValueConverter().getDefaultCelValue(fd); } @Override public Optional find(String field) { - // Per SelectableValue#find, a field that doesn't exist is reported as absent. - return Optional.ofNullable(descriptor().findByFieldName(field)).map(this::readField); + return Optional.ofNullable(readField(findFieldDescriptor(field))); + } + + private FieldLiteDescriptor findFieldDescriptor(String field) { + FieldLiteDescriptor fd = descriptor().findByFieldName(field); + if (fd == null) { + throw CelAttributeNotFoundException.of( + String.format("field '%s' is not declared in message '%s'", field, protoTypeName())); + } + return fd; } @Override diff --git a/common/src/test/java/dev/cel/common/values/ProtoMessageLiteValueTest.java b/common/src/test/java/dev/cel/common/values/ProtoMessageLiteValueTest.java index df5fd1df9..209c448cb 100644 --- a/common/src/test/java/dev/cel/common/values/ProtoMessageLiteValueTest.java +++ b/common/src/test/java/dev/cel/common/values/ProtoMessageLiteValueTest.java @@ -368,11 +368,29 @@ public void select_undeclaredField_throwsCelAttributeNotFoundException() { + " 'cel.expr.conformance.proto3.TestAllTypes'"); } + @Test + public void find_undeclaredField_throwsCelAttributeNotFoundException() { + ProtoMessageLiteValue messageLiteValue = + ProtoMessageLiteValue.create( + TestAllTypes.getDefaultInstance(), + TEST_ALL_TYPES_DESCRIPTOR, + PROTO_LITE_CEL_VALUE_CONVERTER); + + CelAttributeNotFoundException thrown = + assertThrows( + CelAttributeNotFoundException.class, () -> messageLiteValue.find("undeclared_field")); + + assertThat(thrown) + .hasMessageThat() + .isEqualTo( + "field 'undeclared_field' is not declared in message" + + " 'cel.expr.conformance.proto3.TestAllTypes'"); + } + @SuppressWarnings("ImmutableEnumChecker") // Test only private enum FindFieldTestCase { PRESENT("single_string", Optional.of("foo")), - ABSENT("single_int64", Optional.empty()), - UNDECLARED("undeclared_field", Optional.empty()); + ABSENT("single_int64", Optional.empty()); private final String fieldName; private final Optional expected; diff --git a/conformance/src/test/java/dev/cel/conformance/BUILD.bazel b/conformance/src/test/java/dev/cel/conformance/BUILD.bazel index 7c971ef6b..cd0824fb5 100644 --- a/conformance/src/test/java/dev/cel/conformance/BUILD.bazel +++ b/conformance/src/test/java/dev/cel/conformance/BUILD.bazel @@ -1,5 +1,5 @@ load("@rules_java//java:defs.bzl", "java_library") -load("//conformance/src/test/java/dev/cel/conformance:conformance_test.bzl", "MODE", "conformance_test") +load("//conformance/src/test/java/dev/cel/conformance:conformance_test.bzl", "MODE", "RUNTIME", "conformance_test") package(default_applicable_licenses = [ "//:license", @@ -26,17 +26,33 @@ java_library( "//common/ast:cel_block", "//common/types", "//common/types:cel_proto_types", + "//common/types:message_type_provider", + "//common/types:type_providers", + "//common/values:cel_value_provider", + "//common/values:proto_message_lite_value_provider", "//compiler", "//compiler:compiler_builder", "//extensions", "//extensions:bindings", + "//extensions:comprehensions_runtime_library", + "//extensions:encoders_runtime_library", + "//extensions:lists_runtime_library", + "//extensions:math_runtime_library", "//extensions:optional_library", + "//extensions:sets_runtime_library", + "//extensions:strings_runtime_library", "//parser:macro", "//parser:parser_builder", "//parser:parser_factory", "//runtime", + "//runtime:lite_runtime", + "//runtime:lite_runtime_factory", + "//runtime:program", "//runtime:runtime_planner_impl", + "//runtime:standard_functions", "//testing:expr_value_utils", + "//testing/protos:test_all_types_cel_java_proto2", + "//testing/protos:test_all_types_cel_java_proto3", "@cel_spec//proto/cel/expr:expr_java_proto", "@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto", "@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto", @@ -63,10 +79,15 @@ java_library( tags = ["conformance_maven"], deps = MAVEN_JAR_DEPS + [ "//:java_truth", + "//common/values:proto_message_lite_value_provider", "//compiler:compiler_builder", "//parser:parser_factory", + "//runtime:lite_runtime", + "//runtime:lite_runtime_factory", "//runtime:runtime_planner_impl", "//testing:expr_value_utils", + "//testing/protos:test_all_types_cel_java_proto2", + "//testing/protos:test_all_types_cel_java_proto3", "@cel_spec//proto/cel/expr:expr_java_proto", "@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto", "@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto", @@ -168,6 +189,46 @@ _TESTS_TO_SKIP_PLANNER = [ "enums/strong_proto3", ] +_TESTS_TO_SKIP_LITE = _TESTS_TO_SKIP_PLANNER + [ + # Optionals are not supported in CelLiteRuntime. + "optionals", + "block_ext/basic/optional_list,optional_map,optional_map_chained,optional_message", + + # Proto2 extensions are not supported in CelLiteRuntime. + "proto2_ext", + "proto2/extensions_has", + "proto2/extensions_get", + + # TODO: Message creation with prepopulated fields, Any unpacking, and Proto + # Differencer equality are not yet supported in CelLiteRuntime. + "comparisons/eq_literal/eq_dyn_json_null,not_eq_dyn_proto2_msg_null,not_eq_dyn_proto3_msg_null", + "comparisons/eq_wrapper", + "comparisons/ne_literal/ne_proto2,ne_proto3,ne_proto2_missing_fields_neq,ne_proto3_missing_fields_neq,ne_proto_different_types,ne_proto_nan_not_equal", + "comparisons/ne_literal/ne_proto2_any_unpack,ne_proto2_any_unpack_bytewise_fallback,ne_proto3_any_unpack,ne_proto3_any_unpack_bytewise_fallback", + "dynamic", + "enums/legacy_proto2/assign_standalone_name,assign_standalone_int,assign_standalone_int_too_big,assign_standalone_int_too_neg,assign_repeated_enum,list_enum_as_list_int,enum_as_int", + "enums/legacy_proto3/assign_standalone_name,assign_standalone_int,assign_standalone_int_big,assign_standalone_int_neg,assign_standalone_int_too_big,assign_standalone_int_too_neg,assign_repeated_enum,list_enum_as_list_int,enum_as_int", + "parse/nest/message_literal", + "parse/repeat/message_literal", + "parse/whitespace", + "parse/comments", + "parse/struct_field_names", + "proto2/empty_field/scalar_with_default", + "proto2/literal_singular", + "proto2/literal_wellknown", + "proto2/set_null", + "proto2/quoted_fields", + "proto2/has/repeated_none_explicit,repeated_one,repeated_many,map_none_explicit,map_one_default,map_one,map_many,required,optional_set_no_default,optional_set_with_default,optional_set_to_default,optional_message_set,optional_enum_set,optional_enum_set_zero,oneof_other_set,oneof_set,oneof_set_default", + "proto3/literal_singular", + "proto3/literal_wellknown", + "proto3/set_null", + "proto3/quoted_fields", + "proto3/has/repeated_none_explicit,repeated_one,repeated_many,map_none_explicit,map_one_default,map_one,map_many,single_set,single_set_to_default,single_message_set,single_message_set_to_default,single_enum_set,single_enum_set_zero,oneof_other_set,oneof_set,oneof_set_default", + "type_deductions/complex_initializers/struct", + "type_deductions/field_access/int_field", + "wrappers", +] + conformance_test( name = "conformance", data = _ALL_TESTS, @@ -190,6 +251,13 @@ conformance_test( conformance_test( name = "conformance_planner", data = _ALL_TESTS, + runtime = RUNTIME.PLANNER, skip_tests = _TESTS_TO_SKIP_PLANNER, - use_planner = True, +) + +conformance_test( + name = "conformance_lite", + data = _ALL_TESTS, + runtime = RUNTIME.LITE, + skip_tests = _TESTS_TO_SKIP_LITE, ) diff --git a/conformance/src/test/java/dev/cel/conformance/ConformanceTest.java b/conformance/src/test/java/dev/cel/conformance/ConformanceTest.java index 0e7f12f26..06a3fdc43 100644 --- a/conformance/src/test/java/dev/cel/conformance/ConformanceTest.java +++ b/conformance/src/test/java/dev/cel/conformance/ConformanceTest.java @@ -14,6 +14,7 @@ package dev.cel.conformance; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat; import static dev.cel.testing.utils.ExprValueUtils.fromValue; @@ -24,7 +25,6 @@ import dev.cel.expr.MapValue; import dev.cel.expr.Type; import dev.cel.expr.Value; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.protobuf.ExtensionRegistry; @@ -44,13 +44,23 @@ import dev.cel.common.ast.CelConstant; import dev.cel.common.ast.CelExpr; import dev.cel.common.types.CelProtoTypes; +import dev.cel.common.types.CelTypeProvider; +import dev.cel.common.types.ProtoMessageTypeProvider; import dev.cel.common.types.SimpleType; +import dev.cel.common.values.CelValueProvider; +import dev.cel.common.values.ProtoMessageLiteValueProvider; import dev.cel.compiler.CelCompilerFactory; import dev.cel.compiler.CelCompilerLibrary; import dev.cel.expr.conformance.test.SimpleTest; import dev.cel.extensions.CelBindingsExtensions; +import dev.cel.extensions.CelComprehensionsRuntimeLibrary; +import dev.cel.extensions.CelEncoderRuntimeLibrary; import dev.cel.extensions.CelExtensions; +import dev.cel.extensions.CelListsRuntimeLibrary; +import dev.cel.extensions.CelMathRuntimeLibrary; import dev.cel.extensions.CelOptionalLibrary; +import dev.cel.extensions.CelSetsRuntimeLibrary; +import dev.cel.extensions.CelStringRuntimeLibrary; import dev.cel.parser.CelMacro; import dev.cel.parser.CelMacroExpander; import dev.cel.parser.CelMacroExprFactory; @@ -59,12 +69,14 @@ import dev.cel.parser.CelParserFactory; import dev.cel.parser.CelStandardMacro; import dev.cel.runtime.CelEvaluationException; -import dev.cel.runtime.CelRuntime; -import dev.cel.runtime.CelRuntime.Program; +import dev.cel.runtime.CelLiteRuntimeFactory; +import dev.cel.runtime.CelLiteRuntimeLibrary; import dev.cel.runtime.CelRuntimeBuilder; import dev.cel.runtime.CelRuntimeFactory; import dev.cel.runtime.CelRuntimeImpl; import dev.cel.runtime.CelRuntimeLibrary; +import dev.cel.runtime.CelStandardFunctions; +import dev.cel.runtime.Program; import java.util.Map; import java.util.Optional; import org.junit.runners.model.Statement; @@ -104,6 +116,37 @@ public final class ConformanceTest extends Statement { CelExtensions.strings(), CelOptionalLibrary.INSTANCE); + private static final ImmutableList CANONICAL_LITE_RUNTIME_EXTENSIONS = + ImmutableList.of( + CelComprehensionsRuntimeLibrary.comprehensions(), + CelEncoderRuntimeLibrary.encoders(), + CelListsRuntimeLibrary.lists(), + CelMathRuntimeLibrary.math(), + CelSetsRuntimeLibrary.sets(), + CelStringRuntimeLibrary.strings()); + + private static final CelValueProvider LITE_VALUE_PROVIDER = + ProtoMessageLiteValueProvider.newInstance( + dev.cel.expr.conformance.proto2.TestAllTypesCelDescriptor.getDescriptor(), + dev.cel.expr.conformance.proto2.NestedTestAllTypesCelDescriptor.getDescriptor(), + dev.cel.expr.conformance.proto2.TestRequiredCelDescriptor.getDescriptor(), + dev.cel.expr.conformance.proto3.TestAllTypesCelDescriptor.getDescriptor(), + dev.cel.expr.conformance.proto3.NestedTestAllTypesCelDescriptor.getDescriptor()); + + private static final CelTypeProvider LITE_TYPE_PROVIDER = + ProtoMessageTypeProvider.newBuilder() + .addFileDescriptors( + dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor().getFile(), + dev.cel.expr.conformance.proto3.TestAllTypes.getDescriptor().getFile(), + dev.cel.expr.conformance.proto2.TestAllTypesExtensions.getDescriptor()) + .build(); + + enum RuntimeType { + LEGACY, + PLANNER, + LITE + } + static final TypeRegistry CONFORMANCE_TYPE_REGISTRY = TypeRegistry.newBuilder() .add(dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor()) @@ -154,26 +197,6 @@ private static CelChecker getChecker(SimpleTest test) throws Exception { .build(); } - private static CelRuntime getRuntime(SimpleTest test, boolean usePlanner) { - CelRuntimeBuilder builder = - usePlanner ? CelRuntimeImpl.newBuilder() : CelRuntimeFactory.standardCelRuntimeBuilder(); - - builder - // CEL-Internal-2 - .setOptions(OPTIONS) - .addLibraries(CANONICAL_RUNTIME_EXTENSIONS) - .setExtensionRegistry(CONFORMANCE_EXTENSION_REGISTRY) - .addMessageTypes(dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor()) - .addMessageTypes(dev.cel.expr.conformance.proto3.TestAllTypes.getDescriptor()) - .addFileTypes(dev.cel.expr.conformance.proto2.TestAllTypesExtensions.getDescriptor()); - - if (usePlanner) { - builder.setContainer(CelContainer.ofName(test.getContainer())); - } - - return builder.build(); - } - private static ImmutableMap getBindings(SimpleTest test) throws Exception { ImmutableMap.Builder bindings = ImmutableMap.builderWithExpectedSize(test.getBindingsCount()); @@ -204,16 +227,7 @@ private static SimpleTest defaultTestMatcherToTrueIfUnset(SimpleTest test) { private final String name; private final SimpleTest test; private final boolean skip; - private final boolean usePlanner; - - public ConformanceTest(String name, SimpleTest test, boolean skip, boolean usePlanner) { - this.name = Preconditions.checkNotNull(name); - this.test = - Preconditions.checkNotNull( - defaultTestMatcherToTrueIfUnset(Preconditions.checkNotNull(test))); - this.skip = skip; - this.usePlanner = usePlanner; - } + private final RuntimeType runtimeType; public String getName() { return name; @@ -251,16 +265,15 @@ public void evaluate() throws Throwable { return; } - if (!usePlanner && test.getDisableCheck()) { - // Only planner supports parsed-only evaluation + if (runtimeType.equals(RuntimeType.LEGACY) && test.getDisableCheck()) { + // Only planner and lite runtime support parsed-only evaluation return; } - CelRuntime runtime = getRuntime(test, usePlanner); ExprValue result = null; CelEvaluationException error = null; try { - Program program = runtime.createProgram(ast); + Program program = createProgram(ast); result = toExprValue(program.eval(getBindings(test)), ast.getResultType()); } catch (CelEvaluationException e) { error = e; @@ -295,6 +308,48 @@ public void evaluate() throws Throwable { } } + @SuppressWarnings("deprecation") // Intentionally testing the legacy interpreter runtime. + private Program createProgram(CelAbstractSyntaxTree ast) throws CelEvaluationException { + if (runtimeType.equals(RuntimeType.LITE)) { + return CelLiteRuntimeFactory.newLiteRuntimeBuilder() + .setOptions(OPTIONS) + .setStandardFunctions(CelStandardFunctions.ALL_STANDARD_FUNCTIONS) + .addLibraries(CANONICAL_LITE_RUNTIME_EXTENSIONS) + .setValueProvider(LITE_VALUE_PROVIDER) + .setTypeProvider(LITE_TYPE_PROVIDER) + .setContainer(CelContainer.ofName(test.getContainer())) + .build() + .createProgram(ast); + } + + CelRuntimeBuilder builder = + runtimeType.equals(RuntimeType.PLANNER) + ? CelRuntimeImpl.newBuilder() + : CelRuntimeFactory.standardCelRuntimeBuilder(); + + builder + // CEL-Internal-2 + .setOptions(OPTIONS) + .addLibraries(CANONICAL_RUNTIME_EXTENSIONS) + .setExtensionRegistry(CONFORMANCE_EXTENSION_REGISTRY) + .addMessageTypes(dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor()) + .addMessageTypes(dev.cel.expr.conformance.proto3.TestAllTypes.getDescriptor()) + .addFileTypes(dev.cel.expr.conformance.proto2.TestAllTypesExtensions.getDescriptor()); + + if (runtimeType.equals(RuntimeType.PLANNER)) { + builder.setContainer(CelContainer.ofName(test.getContainer())); + } + + return builder.build().createProgram(ast); + } + + ConformanceTest(String name, SimpleTest test, boolean skip, RuntimeType runtimeType) { + this.name = checkNotNull(name); + this.test = checkNotNull(defaultTestMatcherToTrueIfUnset(checkNotNull(test))); + this.skip = skip; + this.runtimeType = checkNotNull(runtimeType); + } + /** * Conformance-only library providing macros for the {@code block_ext} test suite. * diff --git a/conformance/src/test/java/dev/cel/conformance/ConformanceTestRunner.java b/conformance/src/test/java/dev/cel/conformance/ConformanceTestRunner.java index 4c3631d31..80bc07acd 100644 --- a/conformance/src/test/java/dev/cel/conformance/ConformanceTestRunner.java +++ b/conformance/src/test/java/dev/cel/conformance/ConformanceTestRunner.java @@ -15,17 +15,18 @@ package dev.cel.conformance; import static dev.cel.conformance.ConformanceTest.CONFORMANCE_EXTENSION_REGISTRY; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedMap; import com.google.protobuf.TextFormat; +import dev.cel.conformance.ConformanceTest.RuntimeType; import dev.cel.expr.conformance.test.SimpleTest; import dev.cel.expr.conformance.test.SimpleTestFile; import dev.cel.expr.conformance.test.SimpleTestSection; import java.io.BufferedReader; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; @@ -42,7 +43,7 @@ public final class ConformanceTestRunner extends ParentRunner { private final ImmutableSortedMap testFiles; private final ImmutableList testsToSkip; - private final boolean usePlanner; + private final RuntimeType runtimeType; private static ImmutableSortedMap loadTestFiles() { List testPaths = @@ -56,8 +57,7 @@ private static ImmutableSortedMap loadTestFiles() { ImmutableSortedMap.naturalOrder(); for (String testPath : testPaths) { SimpleTestFile.Builder fileBuilder = SimpleTestFile.newBuilder(); - try (BufferedReader input = - Files.newBufferedReader(Paths.get(testPath), StandardCharsets.UTF_8)) { + try (BufferedReader input = Files.newBufferedReader(Paths.get(testPath), UTF_8)) { parser.merge(input, CONFORMANCE_EXTENSION_REGISTRY, fileBuilder); } SimpleTestFile testFile = fileBuilder.build(); @@ -77,9 +77,9 @@ public ConformanceTestRunner(Class clazz) throws InitializationError { ImmutableList.copyOf( SPLITTER.splitToList( System.getProperty("dev.cel.conformance.ConformanceTests.skip_tests"))); - usePlanner = - Boolean.parseBoolean( - System.getProperty("dev.cel.conformance.ConformanceTests.use_planner", "false")); + runtimeType = + RuntimeType.valueOf( + System.getProperty("dev.cel.conformance.ConformanceTests.runtime", "LEGACY")); } private boolean shouldSkipTest(String name) { @@ -102,7 +102,7 @@ protected List getChildren() { for (SimpleTest test : testSection.getTestList()) { String name = String.format("%s/%s/%s", testFile.getName(), testSection.getName(), test.getName()); - tests.add(new ConformanceTest(name, test, shouldSkipTest(name), usePlanner)); + tests.add(new ConformanceTest(name, test, shouldSkipTest(name), runtimeType)); } } } diff --git a/conformance/src/test/java/dev/cel/conformance/conformance_test.bzl b/conformance/src/test/java/dev/cel/conformance/conformance_test.bzl index b91ce4a8b..5d80f96c1 100644 --- a/conformance/src/test/java/dev/cel/conformance/conformance_test.bzl +++ b/conformance/src/test/java/dev/cel/conformance/conformance_test.bzl @@ -38,11 +38,11 @@ def _expand_tests_to_skip(tests_to_skip): result.append(test_to_skip[0:slash] + part) return result -def _conformance_test_args(data, skip_tests, use_planner): +def _conformance_test_args(data, skip_tests, runtime): return [ "-Ddev.cel.conformance.ConformanceTests.skip_tests={}".format(",".join(_expand_tests_to_skip(skip_tests))), "-Ddev.cel.conformance.ConformanceTests.tests={}".format(",".join(["$(location {})".format(t) for t in data])), - "-Ddev.cel.conformance.ConformanceTests.use_planner={}".format("true" if use_planner else "false"), + "-Ddev.cel.conformance.ConformanceTests.runtime={}".format(runtime), ] MODE = struct( @@ -54,7 +54,13 @@ MODE = struct( DASHBOARD = "dashboard", ) -def conformance_test(name, data, mode = MODE.TEST, skip_tests = [], use_planner = False): +RUNTIME = struct( + LEGACY = "LEGACY", + PLANNER = "PLANNER", + LITE = "LITE", +) + +def conformance_test(name, data, mode = MODE.TEST, skip_tests = [], runtime = RUNTIME.LEGACY): """Executes conformance tests Args: @@ -66,11 +72,13 @@ def conformance_test(name, data, mode = MODE.TEST, skip_tests = [], use_planner - `MODE.MAVEN_TEST`: Runs the conformance tests against published JAR in maven central. skip_tests: A list of strings, where each string is the name of a test file to exclude from the run. + runtime: The CEL runtime implementation to evaluate against (`RUNTIME.LEGACY`, + `RUNTIME.PLANNER`, or `RUNTIME.LITE`). """ if mode == MODE.DASHBOARD: java_test( name = "_" + name, - jvm_flags = _conformance_test_args(data, skip_tests, use_planner), + jvm_flags = _conformance_test_args(data, skip_tests, runtime), data = data, size = "small", test_class = "dev.cel.conformance.ConformanceTests", @@ -96,7 +104,7 @@ def conformance_test(name, data, mode = MODE.TEST, skip_tests = [], use_planner elif mode == MODE.TEST: java_test( name = name, - jvm_flags = _conformance_test_args(data, skip_tests, use_planner), + jvm_flags = _conformance_test_args(data, skip_tests, runtime), data = data, size = "small", test_class = "dev.cel.conformance.ConformanceTests", @@ -105,7 +113,7 @@ def conformance_test(name, data, mode = MODE.TEST, skip_tests = [], use_planner elif mode == MODE.MAVEN_TEST: java_test( name = name, - jvm_flags = _conformance_test_args(data, skip_tests, use_planner), + jvm_flags = _conformance_test_args(data, skip_tests, runtime), data = data, size = "small", test_class = "dev.cel.conformance.ConformanceTests", diff --git a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel index eab664280..0042b665e 100644 --- a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel @@ -992,6 +992,7 @@ java_library( ":program", ":runtime_equality", ":runtime_helpers", + ":type_resolver", "//common:cel_ast", "//common:container", "//common:options", @@ -1003,6 +1004,7 @@ java_library( "//runtime:evaluation_exception", "//runtime/planner:program_planner", "//runtime/standard:standard_function", + "//runtime/standard:type", "@maven//:com_google_code_findbugs_annotations", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", @@ -1021,6 +1023,7 @@ cel_android_library( ":program_android", ":runtime_equality_android", ":runtime_helpers_android", + ":type_resolver_android", "//common:cel_ast_android", "//common:container_android", "//common:options", @@ -1032,6 +1035,7 @@ cel_android_library( "//runtime:evaluation_exception", "//runtime/planner:program_planner_android", "//runtime/standard:standard_function_android", + "//runtime/standard:type_android", "@maven//:com_google_code_findbugs_annotations", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", diff --git a/runtime/src/main/java/dev/cel/runtime/CelLiteRuntimeImpl.java b/runtime/src/main/java/dev/cel/runtime/CelLiteRuntimeImpl.java index 46f25ce02..b74bf11e2 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelLiteRuntimeImpl.java +++ b/runtime/src/main/java/dev/cel/runtime/CelLiteRuntimeImpl.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; import javax.annotation.concurrent.ThreadSafe; import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelContainer; @@ -31,6 +32,7 @@ import dev.cel.common.values.CelValueProvider; import dev.cel.runtime.planner.ProgramPlanner; import dev.cel.runtime.standard.CelStandardFunction; +import dev.cel.runtime.standard.TypeFunction; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -189,15 +191,24 @@ public CelLiteRuntime build() { } } + CelTypeProvider celTypeProvider = DefaultTypeProvider.getInstance(); + if (this.celTypeProvider != null) { + celTypeProvider = + new CelTypeProvider.CombinedCelTypeProvider(celTypeProvider, this.celTypeProvider); + } + ImmutableMap.Builder functionBindingsBuilder = ImmutableMap.builder(); ImmutableSet standardFunctions = standardFunctionBuilder.build(); if (!standardFunctions.isEmpty()) { - for (CelStandardFunction standardFunction : standardFunctions) { - ImmutableSet standardFunctionBinding = - standardFunction.newFunctionBindings(celOptions, runtimeEquality); - for (CelFunctionBinding func : standardFunctionBinding) { + TypeFunction typeFunction = + TypeFunction.create( + TypeResolver.create(celTypeProvider, celValueProvider.celValueConverter())); + for (CelStandardFunction standardFunction : + Iterables.concat(standardFunctions, ImmutableList.of(typeFunction))) { + for (CelFunctionBinding func : + standardFunction.newFunctionBindings(celOptions, runtimeEquality)) { functionBindingsBuilder.put(func.getOverloadId(), func); } } @@ -222,12 +233,6 @@ public CelLiteRuntime build() { func.getDefinition()); }); - CelTypeProvider celTypeProvider = DefaultTypeProvider.getInstance(); - if (this.celTypeProvider != null) { - celTypeProvider = - new CelTypeProvider.CombinedCelTypeProvider(celTypeProvider, this.celTypeProvider); - } - ProgramPlanner planner = ProgramPlanner.newPlanner( celTypeProvider, diff --git a/runtime/src/main/java/dev/cel/runtime/TypeResolver.java b/runtime/src/main/java/dev/cel/runtime/TypeResolver.java index 531641d2e..46a72ed82 100644 --- a/runtime/src/main/java/dev/cel/runtime/TypeResolver.java +++ b/runtime/src/main/java/dev/cel/runtime/TypeResolver.java @@ -28,6 +28,7 @@ import com.google.protobuf.Timestamp; import dev.cel.common.annotations.Internal; import dev.cel.common.types.CelType; +import dev.cel.common.types.CelTypeProvider; import dev.cel.common.types.ListType; import dev.cel.common.types.MapType; import dev.cel.common.types.OptionalType; @@ -55,12 +56,17 @@ @Internal public class TypeResolver { + private final CelTypeProvider celTypeProvider; private final CelValueConverter celValueConverter; static TypeResolver create(CelValueConverter celValueConverter) { return new TypeResolver(celValueConverter); } + static TypeResolver create(CelTypeProvider celTypeProvider, CelValueConverter celValueConverter) { + return new TypeResolver(celTypeProvider, celValueConverter); + } + // Sentinel runtime value representing the special "type" ident. This ensures following to be // true: type == type(string) && type == type(type("foo")) @VisibleForTesting static final TypeType RUNTIME_TYPE_TYPE = TypeType.create(SimpleType.DYN); @@ -198,7 +204,8 @@ public TypeType resolveObjectType(Object obj, CelType typeCheckedType) { if (celValueConverter != null) { Object celVal = celValueConverter.toRuntimeValue(obj); if (celVal instanceof CelValue) { - return TypeType.create(((CelValue) celVal).celType()); + CelType celType = ((CelValue) celVal).celType(); + return TypeType.create(celTypeProvider.findType(celType.name()).orElse(celType)); } } @@ -235,6 +242,11 @@ private static CelType adaptStructType(StructType typeOfType) { } protected TypeResolver(CelValueConverter celValueConverter) { + this(new CelTypeProvider.CombinedCelTypeProvider(ImmutableList.of()), celValueConverter); + } + + private TypeResolver(CelTypeProvider celTypeProvider, CelValueConverter celValueConverter) { + this.celTypeProvider = checkNotNull(celTypeProvider); this.celValueConverter = checkNotNull(celValueConverter); } } diff --git a/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeTest.java b/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeTest.java index 79de0b70d..0db4eee46 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeTest.java @@ -20,7 +20,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.primitives.UnsignedLong; import com.google.protobuf.BoolValue; import com.google.protobuf.ByteString; @@ -91,17 +90,16 @@ public class CelLiteRuntimeTest { private static final CelLiteRuntime CEL_RUNTIME = CelLiteRuntimeFactory.newLiteRuntimeBuilder() .setStandardFunctions(CelStandardFunctions.ALL_STANDARD_FUNCTIONS) - .setTypeProvider( - ProtoMessageTypeProvider.newBuilder() - .addDescriptors( - ImmutableSet.of( - dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor(), - TestAllTypes.getDescriptor())) - .build()) .setValueProvider( ProtoMessageLiteValueProvider.newInstance( dev.cel.expr.conformance.proto2.TestAllTypesCelDescriptor.getDescriptor(), TestAllTypesCelDescriptor.getDescriptor())) + .setTypeProvider( + ProtoMessageTypeProvider.newBuilder() + .addFileDescriptors( + dev.cel.expr.conformance.proto2.TestAllTypes.getDescriptor().getFile(), + TestAllTypes.getDescriptor().getFile()) + .build()) .setContainer(CEL_CONTAINER) .build(); @@ -117,9 +115,9 @@ public void messageCreation_emptyMessage() throws Exception { @Test public void messageCreation_fieldsPopulated() throws Exception { CelAbstractSyntaxTree ast = CEL_COMPILER.compile("TestAllTypes{single_int32: 4}").getAst(); + Program program = CEL_RUNTIME.createProgram(ast); - CelEvaluationException e = - assertThrows(CelEvaluationException.class, () -> CEL_RUNTIME.createProgram(ast).eval()); + CelEvaluationException e = assertThrows(CelEvaluationException.class, program::eval); assertThat(e) .hasMessageThat() @@ -226,7 +224,7 @@ public void fieldSelection_repeatedStrings() throws Exception { } @Test - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") // Program.eval returns untyped Object. public void fieldSelection_repeatedBoolWrappers() throws Exception { CelAbstractSyntaxTree ast = CEL_COMPILER.compile("msg.repeated_bool_wrapper").getAst(); TestAllTypes msg = @@ -245,7 +243,7 @@ public void fieldSelection_repeatedBoolWrappers() throws Exception { @Test @TestParameters("{expression: 'msg.map_string_int32'}") @TestParameters("{expression: 'msg.map_string_int64'}") - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") // Program.eval returns untyped Object. public void fieldSelection_map(String expression) throws Exception { CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expression).getAst(); TestAllTypes msg = @@ -340,7 +338,7 @@ public void fieldSelection_timestamp() throws Exception { } @Test - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") // Program.eval returns untyped Object. public void fieldSelection_jsonStruct() throws Exception { String expression = "msg.single_struct"; CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expression).getAst(); @@ -370,7 +368,7 @@ public void fieldSelection_jsonValue() throws Exception { } @Test - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") // Program.eval returns untyped Object. public void fieldSelection_jsonListValue() throws Exception { String expression = "msg.list_value"; CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expression).getAst(); @@ -727,4 +725,20 @@ public void eval_withEnumField() throws Exception { assertThat(result).isEqualTo(SimpleEnum.BAR.getNumber()); } + + @Test + @TestParameters("{expression: 'type(1) == int'}") + @TestParameters("{expression: 'type(TestAllTypes{}) == TestAllTypes'}") + @TestParameters("{expression: 'type(msg) == cel.expr.conformance.proto3.TestAllTypes'}") + public void eval_typeFunction(String expression) throws Exception { + CelAbstractSyntaxTree ast = CEL_COMPILER.compile(expression).getAst(); + + boolean result = + (boolean) + CEL_RUNTIME + .createProgram(ast) + .eval(ImmutableMap.of("msg", TestAllTypes.getDefaultInstance())); + + assertThat(result).isTrue(); + } } diff --git a/testing/src/main/java/dev/cel/testing/utils/BUILD.bazel b/testing/src/main/java/dev/cel/testing/utils/BUILD.bazel index eea56752d..81f7fd0ff 100644 --- a/testing/src/main/java/dev/cel/testing/utils/BUILD.bazel +++ b/testing/src/main/java/dev/cel/testing/utils/BUILD.bazel @@ -16,6 +16,7 @@ java_library( ], deps = [ "//common:cel_descriptors", + "//common/internal:default_instance_message_factory", "//common/internal:proto_time_utils", "//common/types", "//common/types:type_providers", diff --git a/testing/src/main/java/dev/cel/testing/utils/ExprValueUtils.java b/testing/src/main/java/dev/cel/testing/utils/ExprValueUtils.java index 10ab52786..c9b8928cd 100644 --- a/testing/src/main/java/dev/cel/testing/utils/ExprValueUtils.java +++ b/testing/src/main/java/dev/cel/testing/utils/ExprValueUtils.java @@ -30,6 +30,7 @@ import com.google.protobuf.NullValue; import com.google.protobuf.TypeRegistry; import dev.cel.common.CelDescriptors; +import dev.cel.common.internal.DefaultInstanceMessageFactory; import dev.cel.common.internal.ProtoTimeUtils; import dev.cel.common.types.CelType; import dev.cel.common.types.ListType; @@ -91,7 +92,10 @@ public static Object fromValue( "Unknown type, descriptor was not found in registry: " + value.getObjectValue().getTypeUrl()); } - Message prototype = DynamicMessage.getDefaultInstance(descriptor); + Message prototype = + DefaultInstanceMessageFactory.getInstance() + .getPrototype(descriptor) + .orElseGet(() -> DynamicMessage.getDefaultInstance(descriptor)); return prototype .getParserForType() .parseFrom(value.getObjectValue().getValue(), extensionRegistry);