From 6e8a7a22894d5fc070ccc3136cbb91a0b04fe808 Mon Sep 17 00:00:00 2001 From: Sokwhan Huh Date: Thu, 20 Nov 2025 17:31:48 -0800 Subject: [PATCH] Internal Changes PiperOrigin-RevId: 834975953 --- .../main/java/dev/cel/bundle/CelBuilder.java | 3 +- .../test/java/dev/cel/bundle/CelImplTest.java | 38 -- common/BUILD.bazel | 5 + common/exceptions/BUILD.bazel | 6 + .../src/main/java/dev/cel/common/BUILD.bazel | 12 + .../main/java/dev/cel/common/CelOptions.java | 14 - .../dev/cel/common/exceptions/BUILD.bazel | 13 + .../exceptions/CelDuplicateKeyException.java | 31 ++ .../java/dev/cel/common/types/BUILD.bazel | 40 ++ .../cel/common/types/DefaultTypeProvider.java | 2 + .../types/ProtoMessageLiteTypeProvider.java | 120 +++++ .../java/dev/cel/common/values/BUILD.bazel | 10 + .../values/BaseProtoMessageValueProvider.java | 5 +- .../cel/common/values/CelValueConverter.java | 8 +- .../cel/common/values/CelValueProvider.java | 4 + .../values/CombinedCelValueProvider.java | 9 +- .../common/values/ProtoCelValueConverter.java | 8 +- .../values/ProtoLiteCelValueConverter.java | 10 +- .../values/ProtoMessageLiteValueProvider.java | 4 +- .../values/ProtoMessageValueProvider.java | 4 +- .../java/dev/cel/common/values/BUILD.bazel | 4 +- .../ProtoMessageLiteValueProviderTest.java | 2 +- .../cel/common/values/StructValueTest.java | 91 ++-- common/types/BUILD.bazel | 18 + runtime/BUILD.bazel | 38 +- runtime/planner/BUILD.bazel | 6 + .../src/main/java/dev/cel/runtime/BUILD.bazel | 153 +++---- .../cel/runtime/CelLateFunctionBindings.java | 4 + .../cel/runtime/CelLiteRuntimeBuilder.java | 28 ++ .../dev/cel/runtime/CelRuntimeBuilder.java | 18 +- .../dev/cel/runtime/CelRuntimeFactory.java | 12 + .../java/dev/cel/runtime/CelRuntimeImpl.java | 418 ++++++++++++++++++ .../dev/cel/runtime/CelRuntimeLegacyImpl.java | 60 +-- .../runtime/CelValueRuntimeTypeProvider.java | 150 ------- .../dev/cel/runtime/DefaultInterpreter.java | 9 +- .../java/dev/cel/runtime/LiteProgramImpl.java | 58 --- .../java/dev/cel/runtime/LiteRuntimeImpl.java | 114 +++-- .../java/dev/cel/runtime/planner/BUILD.bazel | 417 ++++++++++++++++- .../dev/cel/runtime/planner/EvalConstant.java | 69 +-- .../cel/runtime/planner/EvalCreateList.java | 2 +- .../cel/runtime/planner/EvalCreateMap.java | 17 +- .../dev/cel/runtime/planner/EvalHelpers.java | 13 +- ...java => LocalizedEvaluationException.java} | 18 +- .../cel/runtime/planner/MissingAttribute.java | 2 + .../runtime/planner/PlannedInterpretable.java | 2 - .../cel/runtime/planner/PlannedProgram.java | 9 +- .../planner/PresenceTestQualifier.java | 2 + .../cel/runtime/planner/ProgramPlanner.java | 27 +- .../runtime/planner/RelativeAttribute.java | 4 + .../cel/runtime/planner/StringQualifier.java | 2 + .../src/test/java/dev/cel/runtime/BUILD.bazel | 15 +- .../cel/runtime/CelLiteInterpreterTest.java | 4 +- .../runtime/CelLiteRuntimeAndroidTest.java | 6 +- .../dev/cel/runtime/CelLiteRuntimeTest.java | 15 +- .../cel/runtime/CelRuntimeFactoryTest.java | 6 + .../cel/runtime/CelRuntimeLegacyImplTest.java | 7 +- .../java/dev/cel/runtime/CelRuntimeTest.java | 12 + ...rTest.java => PlannerInterpreterTest.java} | 18 +- .../java/dev/cel/runtime/planner/BUILD.bazel | 1 + .../runtime/planner/ProgramPlannerTest.java | 18 +- runtime/src/test/resources/maps.baseline | 4 +- .../test/resources/maxComprehension.baseline | 6 +- 62 files changed, 1603 insertions(+), 622 deletions(-) create mode 100644 common/src/main/java/dev/cel/common/exceptions/CelDuplicateKeyException.java create mode 100644 common/src/main/java/dev/cel/common/types/ProtoMessageLiteTypeProvider.java create mode 100644 runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java delete mode 100644 runtime/src/main/java/dev/cel/runtime/CelValueRuntimeTypeProvider.java delete mode 100644 runtime/src/main/java/dev/cel/runtime/LiteProgramImpl.java rename runtime/src/main/java/dev/cel/runtime/planner/{StrictErrorException.java => LocalizedEvaluationException.java} (55%) rename runtime/src/test/java/dev/cel/runtime/{CelValueInterpreterTest.java => PlannerInterpreterTest.java} (63%) diff --git a/bundle/src/main/java/dev/cel/bundle/CelBuilder.java b/bundle/src/main/java/dev/cel/bundle/CelBuilder.java index 1dadaeb39..9c0b5d3b2 100644 --- a/bundle/src/main/java/dev/cel/bundle/CelBuilder.java +++ b/bundle/src/main/java/dev/cel/bundle/CelBuilder.java @@ -197,8 +197,7 @@ public interface CelBuilder { * provider will be used first before falling back to the built-in {@link * dev.cel.common.values.ProtoMessageValueProvider} for resolving protobuf messages. * - *

Note that {@link CelOptions#enableCelValue()} must be enabled or this method will be a - * no-op. + *

Note that this option is only supported for planner-based runtime. */ @CanIgnoreReturnValue CelBuilder setValueProvider(CelValueProvider celValueProvider); diff --git a/bundle/src/test/java/dev/cel/bundle/CelImplTest.java b/bundle/src/test/java/dev/cel/bundle/CelImplTest.java index 00b47494d..2154b8eb0 100644 --- a/bundle/src/test/java/dev/cel/bundle/CelImplTest.java +++ b/bundle/src/test/java/dev/cel/bundle/CelImplTest.java @@ -556,24 +556,6 @@ public void program_withVars() throws Exception { assertThat(program.eval(ImmutableMap.of("variable", "hello"))).isEqualTo(true); } - @Test - public void program_withCelValue() throws Exception { - Cel cel = - standardCelBuilderWithMacros() - .setOptions(CelOptions.current().enableCelValue(true).build()) - .addDeclarations( - Decl.newBuilder() - .setName("variable") - .setIdent(IdentDecl.newBuilder().setType(CelProtoTypes.STRING)) - .build()) - .setResultType(SimpleType.BOOL) - .build(); - - CelRuntime.Program program = cel.createProgram(cel.compile("variable == 'hello'").getAst()); - - assertThat(program.eval(ImmutableMap.of("variable", "hello"))).isEqualTo(true); - } - @Test public void program_withProtoVars() throws Exception { Cel cel = @@ -1419,26 +1401,6 @@ public void programAdvanceEvaluation_nestedSelect() throws Exception { .isEqualTo(CelUnknownSet.create(CelAttribute.fromQualifiedIdentifier("com.google.a"))); } - @Test - public void programAdvanceEvaluation_nestedSelect_withCelValue() throws Exception { - Cel cel = - standardCelBuilderWithMacros() - .setOptions( - CelOptions.current().enableUnknownTracking(true).enableCelValue(true).build()) - .addVar("com", MapType.create(SimpleType.STRING, SimpleType.DYN)) - .addFunctionBindings() - .setResultType(SimpleType.BOOL) - .build(); - CelRuntime.Program program = cel.createProgram(cel.compile("com.google.a || false").getAst()); - - assertThat( - program.advanceEvaluation( - UnknownContext.create( - fromMap(ImmutableMap.of()), - ImmutableList.of(CelAttributePattern.fromQualifiedIdentifier("com.google.a"))))) - .isEqualTo(CelUnknownSet.create(CelAttribute.fromQualifiedIdentifier("com.google.a"))); - } - @Test public void programAdvanceEvaluation_argumentMergeErrorPriority() throws Exception { Cel cel = diff --git a/common/BUILD.bazel b/common/BUILD.bazel index 21a124565..bffc1af37 100644 --- a/common/BUILD.bazel +++ b/common/BUILD.bazel @@ -22,6 +22,11 @@ java_library( exports = ["//common/src/main/java/dev/cel/common:container"], ) +cel_android_library( + name = "container_android", + exports = ["//common/src/main/java/dev/cel/common:container_android"], +) + java_library( name = "proto_ast", exports = ["//common/src/main/java/dev/cel/common:proto_ast"], diff --git a/common/exceptions/BUILD.bazel b/common/exceptions/BUILD.bazel index 4e52113e7..3464632d9 100644 --- a/common/exceptions/BUILD.bazel +++ b/common/exceptions/BUILD.bazel @@ -53,6 +53,12 @@ java_library( exports = ["//common/src/main/java/dev/cel/common/exceptions:iteration_budget_exceeded"], ) +java_library( + name = "duplicate_key", + # used_by_android + exports = ["//common/src/main/java/dev/cel/common/exceptions:duplicate_key"], +) + java_library( name = "overload_not_found", # used_by_android diff --git a/common/src/main/java/dev/cel/common/BUILD.bazel b/common/src/main/java/dev/cel/common/BUILD.bazel index ba223d213..f11ba2d63 100644 --- a/common/src/main/java/dev/cel/common/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/BUILD.bazel @@ -341,6 +341,18 @@ java_library( ], ) +cel_android_library( + name = "container_android", + srcs = ["CelContainer.java"], + tags = [ + ], + deps = [ + "//:auto_value", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "operator", srcs = ["Operator.java"], diff --git a/common/src/main/java/dev/cel/common/CelOptions.java b/common/src/main/java/dev/cel/common/CelOptions.java index d39d53803..50db7026c 100644 --- a/common/src/main/java/dev/cel/common/CelOptions.java +++ b/common/src/main/java/dev/cel/common/CelOptions.java @@ -17,7 +17,6 @@ import com.google.auto.value.AutoValue; import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.Immutable; -import dev.cel.common.annotations.Beta; /** * Options to configure how the CEL parser, type-checker, and evaluator behave. @@ -105,8 +104,6 @@ public enum ProtoUnsetFieldOptions { public abstract boolean enableUnknownTracking(); - public abstract boolean enableCelValue(); - public abstract int comprehensionMaxIterations(); public abstract boolean evaluateCanonicalTypesToNativeValues(); @@ -162,7 +159,6 @@ public static Builder newBuilder() { .errorOnDuplicateMapKeys(false) .resolveTypeDependencies(true) .enableUnknownTracking(false) - .enableCelValue(false) .comprehensionMaxIterations(-1) .unwrapWellKnownTypesOnFunctionDispatch(true) .fromProtoUnsetFieldOption(ProtoUnsetFieldOptions.BIND_DEFAULT) @@ -432,16 +428,6 @@ public abstract static class Builder { */ public abstract Builder enableUnknownTracking(boolean value); - /** - * Enables the usage of {@code CelValue} for the runtime. It is a native value representation of - * CEL that wraps Java native objects, and comes with extended capabilities, such as allowing - * value constructs not understood by CEL (ex: POJOs). - * - *

Warning: This option is experimental. - */ - @Beta - public abstract Builder enableCelValue(boolean value); - /** * Limit the total number of iterations permitted within comprehension loops. * diff --git a/common/src/main/java/dev/cel/common/exceptions/BUILD.bazel b/common/src/main/java/dev/cel/common/exceptions/BUILD.bazel index 755e037d9..672238cf0 100644 --- a/common/src/main/java/dev/cel/common/exceptions/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/exceptions/BUILD.bazel @@ -111,6 +111,19 @@ java_library( ], ) +java_library( + name = "duplicate_key", + srcs = ["CelDuplicateKeyException.java"], + # used_by_android + tags = [ + ], + deps = [ + ":runtime_exception", + "//common:error_codes", + "//common/annotations", + ], +) + java_library( name = "overload_not_found", srcs = ["CelOverloadNotFoundException.java"], diff --git a/common/src/main/java/dev/cel/common/exceptions/CelDuplicateKeyException.java b/common/src/main/java/dev/cel/common/exceptions/CelDuplicateKeyException.java new file mode 100644 index 000000000..f4c99d516 --- /dev/null +++ b/common/src/main/java/dev/cel/common/exceptions/CelDuplicateKeyException.java @@ -0,0 +1,31 @@ +// 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.common.exceptions; + +import dev.cel.common.CelErrorCode; +import dev.cel.common.annotations.Internal; + +/** Indicates an attempt to create a map using duplicate keys. */ +@Internal +public final class CelDuplicateKeyException extends CelRuntimeException { + + public static CelDuplicateKeyException of(Object key) { + return new CelDuplicateKeyException(String.format("duplicate map key [%s]", key)); + } + + private CelDuplicateKeyException(String message) { + super(message, CelErrorCode.DUPLICATE_ATTRIBUTE); + } +} diff --git a/common/src/main/java/dev/cel/common/types/BUILD.bazel b/common/src/main/java/dev/cel/common/types/BUILD.bazel index a35a897b8..b079b9612 100644 --- a/common/src/main/java/dev/cel/common/types/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/types/BUILD.bazel @@ -183,6 +183,32 @@ java_library( ], ) +java_library( + name = "message_lite_type_provider", + srcs = [ + "ProtoMessageLiteTypeProvider.java", + ], + tags = [ + ], + deps = [ + "//common/types", + "//common/types:type_providers", + "//protobuf:cel_lite_descriptor", + "@maven//:com_google_guava_guava", + ], +) + +cel_android_library( + name = "message_lite_type_provider_android", + srcs = [ + "ProtoMessageLiteTypeProvider.java", + ], + tags = [ + ], + deps = [ + ], +) + java_library( name = "default_type_provider", srcs = [ @@ -197,6 +223,20 @@ java_library( ], ) +cel_android_library( + name = "default_type_provider_android", + srcs = [ + "DefaultTypeProvider.java", + ], + tags = [ + ], + deps = [ + "//common/types:type_providers_android", + "//common/types:types_android", + "@maven_android//:com_google_guava_guava", + ], +) + cel_android_library( name = "cel_types_android", srcs = ["CelTypes.java"], diff --git a/common/src/main/java/dev/cel/common/types/DefaultTypeProvider.java b/common/src/main/java/dev/cel/common/types/DefaultTypeProvider.java index 84e6c9ede..f72191e4e 100644 --- a/common/src/main/java/dev/cel/common/types/DefaultTypeProvider.java +++ b/common/src/main/java/dev/cel/common/types/DefaultTypeProvider.java @@ -16,9 +16,11 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.Immutable; import java.util.Optional; /** {@code DefaultTypeProvider} is a registry of common CEL types. */ +@Immutable public class DefaultTypeProvider implements CelTypeProvider { private static final DefaultTypeProvider INSTANCE = new DefaultTypeProvider(); diff --git a/common/src/main/java/dev/cel/common/types/ProtoMessageLiteTypeProvider.java b/common/src/main/java/dev/cel/common/types/ProtoMessageLiteTypeProvider.java new file mode 100644 index 000000000..793943f1b --- /dev/null +++ b/common/src/main/java/dev/cel/common/types/ProtoMessageLiteTypeProvider.java @@ -0,0 +1,120 @@ +// Copyright 2025 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.common.types; + +import static com.google.common.collect.ImmutableMap.toImmutableMap; + +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import dev.cel.protobuf.CelLiteDescriptor; +import dev.cel.protobuf.CelLiteDescriptor.FieldLiteDescriptor; +import dev.cel.protobuf.CelLiteDescriptor.MessageLiteDescriptor; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; + +/** TODO: Add */ +public final class ProtoMessageLiteTypeProvider implements CelTypeProvider { + private static final ImmutableMap PROTO_TYPE_TO_CEL_TYPE = + ImmutableMap.builder() + .put(FieldLiteDescriptor.Type.DOUBLE, SimpleType.DOUBLE) + .put(FieldLiteDescriptor.Type.FLOAT, SimpleType.DOUBLE) + .put(FieldLiteDescriptor.Type.INT64, SimpleType.INT) + .put(FieldLiteDescriptor.Type.INT32, SimpleType.INT) + .put(FieldLiteDescriptor.Type.SFIXED32, SimpleType.INT) + .put(FieldLiteDescriptor.Type.SFIXED64, SimpleType.INT) + .put(FieldLiteDescriptor.Type.SINT32, SimpleType.INT) + .put(FieldLiteDescriptor.Type.SINT64, SimpleType.INT) + .put(FieldLiteDescriptor.Type.BOOL, SimpleType.BOOL) + .put(FieldLiteDescriptor.Type.STRING, SimpleType.STRING) + .put(FieldLiteDescriptor.Type.BYTES, SimpleType.BYTES) + .put(FieldLiteDescriptor.Type.FIXED32, SimpleType.UINT) + .put(FieldLiteDescriptor.Type.FIXED64, SimpleType.UINT) + .put(FieldLiteDescriptor.Type.UINT32, SimpleType.UINT) + .put(FieldLiteDescriptor.Type.UINT64, SimpleType.UINT) + .buildOrThrow(); + + private final ImmutableMap allTypes; + + @Override + public ImmutableCollection types() { + return allTypes.values(); + } + + @Override + public Optional findType(String typeName) { + return Optional.empty(); + } + + public static ProtoMessageLiteTypeProvider newInstance(CelLiteDescriptor... celLiteDescriptors) { + return newInstance(ImmutableSet.copyOf(celLiteDescriptors)); + } + + public static ProtoMessageLiteTypeProvider newInstance( + Set celLiteDescriptors) { + return new ProtoMessageLiteTypeProvider(celLiteDescriptors); + } + + private ProtoMessageLiteTypeProvider(Set celLiteDescriptors) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (CelLiteDescriptor descriptor : celLiteDescriptors) { + for (Entry entry : + descriptor.getProtoTypeNamesToDescriptors().entrySet()) { + builder.put(entry.getKey(), createMessageType(entry.getValue())); + } + } + + this.allTypes = builder.buildOrThrow(); + } + + private static ProtoMessageType createMessageType(MessageLiteDescriptor messageLiteDescriptor) { + ImmutableMap fields = + messageLiteDescriptor.getFieldDescriptors().stream() + .collect(toImmutableMap(FieldLiteDescriptor::getFieldName, Function.identity())); + + return new ProtoMessageType( + messageLiteDescriptor.getProtoTypeName(), + fields.keySet(), + new FieldResolver(fields), + extensionFieldName -> { + throw new UnsupportedOperationException( + "Proto extensions are not yet supported in MessageLite."); + }); + } + + private static class FieldResolver implements StructType.FieldResolver { + private final ImmutableMap fields; + + @Override + public Optional findField(String fieldName) { + FieldLiteDescriptor fieldDescriptor = fields.get(fieldName); + if (fieldDescriptor == null) { + return Optional.empty(); + } + + FieldLiteDescriptor.Type fieldType = fieldDescriptor.getProtoFieldType(); + switch (fieldDescriptor.getProtoFieldType()) { + default: + return Optional.of(PROTO_TYPE_TO_CEL_TYPE.get(fieldType)); + } + } + + private FieldResolver(ImmutableMap fields) { + this.fields = fields; + } + } +} diff --git a/common/src/main/java/dev/cel/common/values/BUILD.bazel b/common/src/main/java/dev/cel/common/values/BUILD.bazel index e9b4be4f1..c52a22c5f 100644 --- a/common/src/main/java/dev/cel/common/values/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/values/BUILD.bazel @@ -57,6 +57,7 @@ java_library( tags = [ ], deps = [ + ":values", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", ], @@ -68,6 +69,7 @@ cel_android_library( tags = [ ], deps = [ + "//common/values:values_android", "@maven//:com_google_errorprone_error_prone_annotations", "@maven_android//:com_google_guava_guava", ], @@ -79,6 +81,7 @@ java_library( tags = [ ], deps = [ + "//common/values", "//common/values:cel_value_provider", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", @@ -92,6 +95,7 @@ cel_android_library( ], deps = [ "//common/values:cel_value_provider_android", + "//common/values:values_android", "@maven//:com_google_errorprone_error_prone_annotations", "@maven_android//:com_google_guava_guava", ], @@ -213,7 +217,9 @@ java_library( "//common/annotations", "//common/internal:dynamic_proto", "//common/internal:proto_message_factory", + "//common/values", "//common/values:base_proto_cel_value_converter", + "//common/values:cel_value_provider", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_protobuf_protobuf_java", ], @@ -284,7 +290,9 @@ java_library( "//common/annotations", "//common/internal:cel_lite_descriptor_pool", "//common/internal:default_lite_descriptor_pool", + "//common/values", "//common/values:base_proto_cel_value_converter", + "//common/values:cel_value_provider", "//protobuf:cel_lite_descriptor", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", @@ -304,6 +312,8 @@ cel_android_library( "//common/internal:cel_lite_descriptor_pool_android", "//common/internal:default_lite_descriptor_pool_android", "//common/values:base_proto_cel_value_converter_android", + "//common/values:cel_value_provider_android", + "//common/values:values_android", "//protobuf:cel_lite_descriptor", "@maven//:com_google_errorprone_error_prone_annotations", "@maven_android//:com_google_guava_guava", diff --git a/common/src/main/java/dev/cel/common/values/BaseProtoMessageValueProvider.java b/common/src/main/java/dev/cel/common/values/BaseProtoMessageValueProvider.java index f42a16179..f9b7a6ce4 100644 --- a/common/src/main/java/dev/cel/common/values/BaseProtoMessageValueProvider.java +++ b/common/src/main/java/dev/cel/common/values/BaseProtoMessageValueProvider.java @@ -25,7 +25,4 @@ */ @Internal @Immutable -public abstract class BaseProtoMessageValueProvider implements CelValueProvider { - - public abstract BaseProtoCelValueConverter protoCelValueConverter(); -} +public abstract class BaseProtoMessageValueProvider implements CelValueProvider {} 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 c3f3727a1..ae0b40ef7 100644 --- a/common/src/main/java/dev/cel/common/values/CelValueConverter.java +++ b/common/src/main/java/dev/cel/common/values/CelValueConverter.java @@ -33,7 +33,13 @@ @SuppressWarnings("unchecked") // Unchecked cast of generics due to type-erasure (ex: MapValue). @Internal @Immutable -public abstract class CelValueConverter { +public class CelValueConverter { + + private static final CelValueConverter DEFAULT_INSTANCE = new CelValueConverter(); + + public static CelValueConverter getDefaultInstance() { + return DEFAULT_INSTANCE; + } /** Adapts a {@link CelValue} to a plain old Java Object. */ public Object unwrap(CelValue celValue) { diff --git a/common/src/main/java/dev/cel/common/values/CelValueProvider.java b/common/src/main/java/dev/cel/common/values/CelValueProvider.java index 717834660..20ae865e7 100644 --- a/common/src/main/java/dev/cel/common/values/CelValueProvider.java +++ b/common/src/main/java/dev/cel/common/values/CelValueProvider.java @@ -27,4 +27,8 @@ public interface CelValueProvider { * a wrapper. */ Optional newValue(String structType, Map fields); + + default CelValueConverter celValueConverter() { + return CelValueConverter.getDefaultInstance(); + } } diff --git a/common/src/main/java/dev/cel/common/values/CombinedCelValueProvider.java b/common/src/main/java/dev/cel/common/values/CombinedCelValueProvider.java index 8fe62cb7b..6aff39a45 100644 --- a/common/src/main/java/dev/cel/common/values/CombinedCelValueProvider.java +++ b/common/src/main/java/dev/cel/common/values/CombinedCelValueProvider.java @@ -30,6 +30,7 @@ @Immutable public final class CombinedCelValueProvider implements CelValueProvider { private final ImmutableList celValueProviders; + private final CelValueConverter celValueConverter; /** Combines the provided first and second {@link CelValueProvider}. */ public static CombinedCelValueProvider combine(CelValueProvider... providers) { @@ -49,12 +50,18 @@ public Optional newValue(String structType, Map fields) return Optional.empty(); } + @Override + public CelValueConverter celValueConverter() { + return celValueConverter; + } + /** Returns the underlying {@link CelValueProvider}s in order. */ public ImmutableList valueProviders() { return celValueProviders; } private CombinedCelValueProvider(ImmutableList providers) { - celValueProviders = checkNotNull(providers); + this.celValueProviders = checkNotNull(providers); + this.celValueConverter = providers.get(0).celValueConverter(); } } diff --git a/common/src/main/java/dev/cel/common/values/ProtoCelValueConverter.java b/common/src/main/java/dev/cel/common/values/ProtoCelValueConverter.java index 08f30b9d1..9400ae961 100644 --- a/common/src/main/java/dev/cel/common/values/ProtoCelValueConverter.java +++ b/common/src/main/java/dev/cel/common/values/ProtoCelValueConverter.java @@ -82,7 +82,13 @@ public Object toRuntimeValue(Object value) { } if (value instanceof MessageOrBuilder) { - MessageOrBuilder message = (MessageOrBuilder) value; + Message message; + if (value instanceof Message.Builder) { + message = ((Message.Builder) value).build(); + } else { + message = (Message) value; + } + // Attempt to convert the proto from a dynamic message into a concrete message if possible. if (message instanceof DynamicMessage) { message = dynamicProto.maybeAdaptDynamicMessage((DynamicMessage) message); diff --git a/common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java b/common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java index 3fbb0ad75..e064e1f48 100644 --- a/common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java +++ b/common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java @@ -28,6 +28,7 @@ import com.google.protobuf.CodedInputStream; import com.google.protobuf.ExtensionRegistryLite; import com.google.protobuf.MessageLite; +import com.google.protobuf.MessageLiteOrBuilder; import com.google.protobuf.WireFormat; import dev.cel.common.annotations.Internal; import dev.cel.common.internal.CelLiteDescriptorPool; @@ -163,8 +164,13 @@ Object getDefaultCelValue(String protoTypeName, String fieldName) { @SuppressWarnings("LiteProtoToString") // No alternative identifier to use. Debug only info is OK. public Object toRuntimeValue(Object value) { checkNotNull(value); - if (value instanceof MessageLite) { - MessageLite msg = (MessageLite) value; + if (value instanceof MessageLiteOrBuilder) { + MessageLite msg; + if (value instanceof MessageLite.Builder) { + msg = ((MessageLite.Builder) value).build(); + } else { + msg = (MessageLite) value; + } MessageLiteDescriptor descriptor = descriptorPool diff --git a/common/src/main/java/dev/cel/common/values/ProtoMessageLiteValueProvider.java b/common/src/main/java/dev/cel/common/values/ProtoMessageLiteValueProvider.java index 041d850a6..b3ee2ef04 100644 --- a/common/src/main/java/dev/cel/common/values/ProtoMessageLiteValueProvider.java +++ b/common/src/main/java/dev/cel/common/values/ProtoMessageLiteValueProvider.java @@ -32,12 +32,12 @@ */ @Immutable @Beta -public class ProtoMessageLiteValueProvider extends BaseProtoMessageValueProvider { +public class ProtoMessageLiteValueProvider implements CelValueProvider { private final CelLiteDescriptorPool descriptorPool; private final ProtoLiteCelValueConverter protoLiteCelValueConverter; @Override - public BaseProtoCelValueConverter protoCelValueConverter() { + public CelValueConverter celValueConverter() { return protoLiteCelValueConverter; } diff --git a/common/src/main/java/dev/cel/common/values/ProtoMessageValueProvider.java b/common/src/main/java/dev/cel/common/values/ProtoMessageValueProvider.java index 5bf2927ab..a05658c8f 100644 --- a/common/src/main/java/dev/cel/common/values/ProtoMessageValueProvider.java +++ b/common/src/main/java/dev/cel/common/values/ProtoMessageValueProvider.java @@ -34,13 +34,13 @@ */ @Immutable @Internal -public class ProtoMessageValueProvider extends BaseProtoMessageValueProvider { +public class ProtoMessageValueProvider implements CelValueProvider { private final ProtoAdapter protoAdapter; private final ProtoMessageFactory protoMessageFactory; private final ProtoCelValueConverter protoCelValueConverter; @Override - public BaseProtoCelValueConverter protoCelValueConverter() { + public CelValueConverter celValueConverter() { return protoCelValueConverter; } diff --git a/common/src/test/java/dev/cel/common/values/BUILD.bazel b/common/src/test/java/dev/cel/common/values/BUILD.bazel index ab7eae8dd..d1651379a 100644 --- a/common/src/test/java/dev/cel/common/values/BUILD.bazel +++ b/common/src/test/java/dev/cel/common/values/BUILD.bazel @@ -9,7 +9,6 @@ java_library( srcs = glob(["*.java"]), deps = [ "//:java_truth", - "//bundle:cel", "//common:cel_ast", "//common:cel_descriptor_util", "//common:options", @@ -29,6 +28,9 @@ java_library( "//common/values:proto_message_lite_value_provider", "//common/values:proto_message_value", "//common/values:proto_message_value_provider", + "//compiler", + "//compiler:compiler_builder", + "//runtime", "//testing/protos:test_all_types_cel_java_proto3", "@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto", "@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto", diff --git a/common/src/test/java/dev/cel/common/values/ProtoMessageLiteValueProviderTest.java b/common/src/test/java/dev/cel/common/values/ProtoMessageLiteValueProviderTest.java index bbe116c80..95e0e031c 100644 --- a/common/src/test/java/dev/cel/common/values/ProtoMessageLiteValueProviderTest.java +++ b/common/src/test/java/dev/cel/common/values/ProtoMessageLiteValueProviderTest.java @@ -49,6 +49,6 @@ public void newValue_emptyFields_success() { @Test public void getProtoLiteCelValueConverter() { - assertThat(VALUE_PROVIDER.protoCelValueConverter()).isNotNull(); + assertThat(VALUE_PROVIDER.celValueConverter()).isNotNull(); } } diff --git a/common/src/test/java/dev/cel/common/values/StructValueTest.java b/common/src/test/java/dev/cel/common/values/StructValueTest.java index 1db147882..deb8e9be0 100644 --- a/common/src/test/java/dev/cel/common/values/StructValueTest.java +++ b/common/src/test/java/dev/cel/common/values/StructValueTest.java @@ -22,8 +22,6 @@ import com.google.common.collect.ImmutableSet; import com.google.testing.junit.testparameterinjector.TestParameterInjector; import com.google.testing.junit.testparameterinjector.TestParameters; -import dev.cel.bundle.Cel; -import dev.cel.bundle.CelFactory; import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelOptions; import dev.cel.common.internal.DynamicProto; @@ -31,7 +29,11 @@ import dev.cel.common.types.CelTypeProvider; import dev.cel.common.types.SimpleType; import dev.cel.common.types.StructType; +import dev.cel.compiler.CelCompiler; +import dev.cel.compiler.CelCompilerFactory; import dev.cel.expr.conformance.proto3.TestAllTypes; +import dev.cel.runtime.CelRuntime; +import dev.cel.runtime.CelRuntimeFactory; import java.util.Map; import java.util.Optional; import org.junit.Test; @@ -115,72 +117,92 @@ public void celTypeTest() { @Test public void evaluate_usingCustomClass_createNewStruct() throws Exception { - Cel cel = - CelFactory.standardCelBuilder() - .setOptions(CelOptions.current().enableCelValue(true).build()) + CelCompiler compiler = + CelCompilerFactory.standardCelCompilerBuilder() + .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) + .build(); + CelRuntime plannerRuntime = + CelRuntimeFactory.plannerCelRuntimeBuilder() .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) .setValueProvider(CUSTOM_STRUCT_VALUE_PROVIDER) .build(); - CelAbstractSyntaxTree ast = cel.compile("custom_struct{data: 50}").getAst(); + CelAbstractSyntaxTree ast = compiler.compile("custom_struct{data: 50}").getAst(); - CelCustomStructValue result = (CelCustomStructValue) cel.createProgram(ast).eval(); + CelCustomStructValue result = (CelCustomStructValue) plannerRuntime.createProgram(ast).eval(); assertThat(result.data).isEqualTo(50); } @Test public void evaluate_usingCustomClass_asVariable() throws Exception { - Cel cel = - CelFactory.standardCelBuilder() - .setOptions(CelOptions.current().enableCelValue(true).build()) + CelCompiler compiler = + CelCompilerFactory.standardCelCompilerBuilder() .addVar("a", CUSTOM_STRUCT_TYPE) + .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) + .build(); + CelRuntime plannerRuntime = + CelRuntimeFactory.plannerCelRuntimeBuilder() .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) .setValueProvider(CUSTOM_STRUCT_VALUE_PROVIDER) .build(); - CelAbstractSyntaxTree ast = cel.compile("a").getAst(); + CelAbstractSyntaxTree ast = compiler.compile("a").getAst(); CelCustomStructValue result = (CelCustomStructValue) - cel.createProgram(ast).eval(ImmutableMap.of("a", new CelCustomStructValue(10))); + plannerRuntime + .createProgram(ast) + .eval(ImmutableMap.of("a", new CelCustomStructValue(10))); assertThat(result.data).isEqualTo(10); } @Test public void evaluate_usingCustomClass_asVariableSelectField() throws Exception { - Cel cel = - CelFactory.standardCelBuilder() - .setOptions(CelOptions.current().enableCelValue(true).build()) + CelCompiler compiler = + CelCompilerFactory.standardCelCompilerBuilder() .addVar("a", CUSTOM_STRUCT_TYPE) + .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) + .build(); + CelRuntime plannerRuntime = + CelRuntimeFactory.plannerCelRuntimeBuilder() .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) .setValueProvider(CUSTOM_STRUCT_VALUE_PROVIDER) .build(); - CelAbstractSyntaxTree ast = cel.compile("a.data").getAst(); + CelAbstractSyntaxTree ast = compiler.compile("a.data").getAst(); - assertThat(cel.createProgram(ast).eval(ImmutableMap.of("a", new CelCustomStructValue(20)))) + assertThat( + plannerRuntime + .createProgram(ast) + .eval(ImmutableMap.of("a", new CelCustomStructValue(20)))) .isEqualTo(20L); } @Test public void evaluate_usingCustomClass_selectField() throws Exception { - Cel cel = - CelFactory.standardCelBuilder() - .setOptions(CelOptions.current().enableCelValue(true).build()) + CelCompiler compiler = + CelCompilerFactory.standardCelCompilerBuilder() + .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) + .build(); + CelRuntime plannerRuntime = + CelRuntimeFactory.plannerCelRuntimeBuilder() .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) .setValueProvider(CUSTOM_STRUCT_VALUE_PROVIDER) .build(); - CelAbstractSyntaxTree ast = cel.compile("custom_struct{data: 5}.data").getAst(); + CelAbstractSyntaxTree ast = compiler.compile("custom_struct{data: 5}.data").getAst(); - Object result = cel.createProgram(ast).eval(); + Object result = plannerRuntime.createProgram(ast).eval(); assertThat(result).isEqualTo(5L); } @Test public void evaluate_usingMultipleProviders_selectFieldFromCustomClass() throws Exception { - Cel cel = - CelFactory.standardCelBuilder() - .setOptions(CelOptions.current().enableCelValue(true).build()) + CelCompiler compiler = + CelCompilerFactory.standardCelCompilerBuilder() + .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) + .build(); + CelRuntime plannerRuntime = + CelRuntimeFactory.plannerCelRuntimeBuilder() .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) .setValueProvider( CombinedCelValueProvider.combine( @@ -188,18 +210,22 @@ public void evaluate_usingMultipleProviders_selectFieldFromCustomClass() throws CelOptions.DEFAULT, DynamicProto.create(typeName -> Optional.empty())), CUSTOM_STRUCT_VALUE_PROVIDER)) .build(); - CelAbstractSyntaxTree ast = cel.compile("custom_struct{data: 5}.data").getAst(); + CelAbstractSyntaxTree ast = compiler.compile("custom_struct{data: 5}.data").getAst(); - Object result = cel.createProgram(ast).eval(); + Object result = plannerRuntime.createProgram(ast).eval(); assertThat(result).isEqualTo(5L); } @Test public void evaluate_usingMultipleProviders_selectFieldFromProtobufMessage() throws Exception { - Cel cel = - CelFactory.standardCelBuilder() - .setOptions(CelOptions.current().enableCelValue(true).build()) + CelCompiler compiler = + CelCompilerFactory.standardCelCompilerBuilder() + .addMessageTypes(TestAllTypes.getDescriptor()) + .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) + .build(); + CelRuntime plannerRuntime = + CelRuntimeFactory.plannerCelRuntimeBuilder() .addMessageTypes(TestAllTypes.getDescriptor()) .setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER) .setValueProvider( @@ -218,10 +244,11 @@ public void evaluate_usingMultipleProviders_selectFieldFromProtobufMessage() thr CUSTOM_STRUCT_VALUE_PROVIDER)) .build(); CelAbstractSyntaxTree ast = - cel.compile("cel.expr.conformance.proto3.TestAllTypes{single_string: 'foo'}.single_string") + compiler + .compile("cel.expr.conformance.proto3.TestAllTypes{single_string: 'foo'}.single_string") .getAst(); - String result = (String) cel.createProgram(ast).eval(); + String result = (String) plannerRuntime.createProgram(ast).eval(); assertThat(result).isEqualTo("foo"); } diff --git a/common/types/BUILD.bazel b/common/types/BUILD.bazel index 41d3d59b2..7d00c8307 100644 --- a/common/types/BUILD.bazel +++ b/common/types/BUILD.bazel @@ -35,6 +35,18 @@ java_library( exports = ["//common/src/main/java/dev/cel/common/types:message_type_provider"], ) +java_library( + name = "message_lite_type_provider", + visibility = ["//:internal"], + exports = ["//common/src/main/java/dev/cel/common/types:message_lite_type_provider"], +) + +cel_android_library( + name = "message_lite_type_provider_android", + visibility = ["//:internal"], + exports = ["//common/src/main/java/dev/cel/common/types:message_lite_type_provider_android"], +) + java_library( name = "cel_types", exports = ["//common/src/main/java/dev/cel/common/types:cel_types"], @@ -56,6 +68,12 @@ java_library( exports = ["//common/src/main/java/dev/cel/common/types:default_type_provider"], ) +cel_android_library( + name = "default_type_provider_android", + visibility = ["//:internal"], + exports = ["//common/src/main/java/dev/cel/common/types:default_type_provider_android"], +) + java_library( name = "cel_v1alpha1_types", visibility = ["//:internal"], diff --git a/runtime/BUILD.bazel b/runtime/BUILD.bazel index 244145960..709b509f1 100644 --- a/runtime/BUILD.bazel +++ b/runtime/BUILD.bazel @@ -28,6 +28,14 @@ java_library( ], ) +cel_android_library( + name = "dispatcher_android", + visibility = ["//:internal"], + exports = [ + "//runtime/src/main/java/dev/cel/runtime:dispatcher_android", + ], +) + java_library( name = "standard_functions", exports = [ @@ -43,6 +51,14 @@ java_library( ], ) +cel_android_library( + name = "activation_android", + visibility = ["//:internal"], + exports = [ + "//runtime/src/main/java/dev/cel/runtime:activation_android", + ], +) + java_library( name = "proto_message_activation_factory", visibility = ["//:internal"], @@ -79,6 +95,7 @@ cel_android_library( java_library( name = "evaluation_exception_builder", + # used_by_android exports = ["//runtime/src/main/java/dev/cel/runtime:evaluation_exception_builder"], ) @@ -112,6 +129,12 @@ java_library( exports = ["//runtime/src/main/java/dev/cel/runtime:interpretable"], ) +cel_android_library( + name = "interpretable_android", + visibility = ["//:internal"], + exports = ["//runtime/src/main/java/dev/cel/runtime:interpretable_android"], +) + java_library( name = "runtime_helpers", visibility = ["//:internal"], @@ -253,21 +276,22 @@ cel_android_library( java_library( name = "metadata", + # used_by_android visibility = ["//:internal"], exports = ["//runtime/src/main/java/dev/cel/runtime:metadata"], ) java_library( - name = "concatenated_list_view", - visibility = ["//:internal"], - exports = [ - "//runtime/src/main/java/dev/cel/runtime:concatenated_list_view", - ], + name = "variable_resolver", + # used_by_android + exports = ["//runtime/src/main/java/dev/cel/runtime:variable_resolver"], ) java_library( - name = "variable_resolver", + name = "concatenated_list_view", + # used_by_android + visibility = ["//:internal"], exports = [ - "//runtime/src/main/java/dev/cel/runtime:variable_resolver", + "//runtime/src/main/java/dev/cel/runtime:concatenated_list_view", ], ) diff --git a/runtime/planner/BUILD.bazel b/runtime/planner/BUILD.bazel index 8da29f270..b76391b54 100644 --- a/runtime/planner/BUILD.bazel +++ b/runtime/planner/BUILD.bazel @@ -1,4 +1,5 @@ load("@rules_java//java:defs.bzl", "java_library") +load("//:cel_android_rules.bzl", "cel_android_library") package( default_applicable_licenses = ["//:license"], @@ -9,3 +10,8 @@ java_library( name = "program_planner", exports = ["//runtime/src/main/java/dev/cel/runtime/planner:program_planner"], ) + +cel_android_library( + name = "program_planner_android", + exports = ["//runtime/src/main/java/dev/cel/runtime/planner:program_planner_android"], +) diff --git a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel index d253edba3..70d95383b 100644 --- a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel @@ -44,11 +44,6 @@ LITE_RUNTIME_IMPL_SOURCES = [ "LiteRuntimeImpl.java", ] -# keep sorted -LITE_PROGRAM_IMPL_SOURCES = [ - "LiteProgramImpl.java", -] - # keep sorted FUNCTION_BINDING_SOURCES = [ "CelFunctionBinding.java", @@ -139,7 +134,8 @@ java_library( cel_android_library( name = "dispatcher_android", srcs = DISPATCHER_SOURCES, - visibility = ["//visibility:private"], + tags = [ + ], deps = [ ":evaluation_exception", ":evaluation_exception_builder", @@ -175,7 +171,8 @@ java_library( cel_android_library( name = "activation_android", srcs = ["Activation.java"], - visibility = ["//visibility:private"], + tags = [ + ], deps = [ ":interpretable_android", ":runtime_helpers_android", @@ -477,6 +474,7 @@ RUNTIME_SOURCES = [ "CelRuntime.java", "CelRuntimeBuilder.java", "CelRuntimeFactory.java", + "CelRuntimeImpl.java", "CelRuntimeLegacyImpl.java", "CelRuntimeLibrary.java", "ProgramImpl.java", @@ -570,6 +568,8 @@ java_library( name = "metadata", srcs = ["Metadata.java"], # used_by_android + tags = [ + ], deps = [ "//common/annotations", "@maven//:com_google_errorprone_error_prone_annotations", @@ -579,11 +579,13 @@ java_library( java_library( name = "interpretable", srcs = INTERPRABLE_SOURCES, + tags = [ + ], deps = [ ":evaluation_exception", - ":evaluation_listener", ":function_resolver", "//common/annotations", + "//runtime:evaluation_listener", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:org_jspecify_jspecify", ], @@ -592,7 +594,8 @@ java_library( cel_android_library( name = "interpretable_android", srcs = INTERPRABLE_SOURCES, - visibility = ["//visibility:private"], + tags = [ + ], deps = [ ":evaluation_exception", ":evaluation_listener_android", @@ -808,7 +811,6 @@ java_library( ], deps = [ ":activation", - ":cel_value_runtime_type_provider", ":descriptor_message_provider", ":descriptor_type_resolver", ":dispatcher", @@ -825,10 +827,12 @@ java_library( ":runtime_type_provider", ":standard_functions", ":unknown_attributes", + ":variable_resolver", "//:auto_value", "//common:cel_ast", "//common:cel_descriptor_util", "//common:cel_descriptors", + "//common:container", "//common:options", "//common/annotations", "//common/internal:cel_descriptor_pools", @@ -836,9 +840,16 @@ java_library( "//common/internal:dynamic_proto", "//common/internal:proto_message_factory", "//common/types:cel_types", + "//common/types:default_type_provider", + "//common/types:message_type_provider", + "//common/types:type_providers", + "//common/values", "//common/values:cel_value_provider", + "//common/values:combined_cel_value_provider", "//common/values:proto_message_value_provider", + "//runtime:proto_message_runtime_helpers", "//runtime:variable_resolver", + "//runtime/planner:program_planner", "//runtime/standard:add", "//runtime/standard:int", "//runtime/standard:timestamp", @@ -861,8 +872,10 @@ java_library( ":program", "//:auto_value", "//common:cel_ast", + "//common:container", "//common:options", "//common/annotations", + "//common/types:type_providers", "//common/values:cel_value_provider", "//runtime/standard:standard_function", "@maven//:com_google_code_findbugs_annotations", @@ -877,53 +890,27 @@ java_library( tags = [ ], deps = [ - ":cel_value_runtime_type_provider", ":dispatcher", ":function_binding", - ":interpreter", - ":lite_program_impl", ":lite_runtime", ":program", ":runtime_equality", ":runtime_helpers", - ":type_resolver", "//:auto_value", "//common:cel_ast", + "//common:container", "//common:options", + "//common/types:default_type_provider", + "//common/types:type_providers", + "//common/values", + "//common/values:cel_value", "//common/values:cel_value_provider", + "//runtime:evaluation_exception", + "//runtime/planner:program_planner", "//runtime/standard:standard_function", "@maven//:com_google_code_findbugs_annotations", - "@maven//:com_google_guava_guava", - ], -) - -java_library( - name = "lite_program_impl", - srcs = LITE_PROGRAM_IMPL_SOURCES, - deps = [ - ":activation", - ":evaluation_exception", - ":function_resolver", - ":interpretable", - ":program", - "//:auto_value", - "//runtime:variable_resolver", - "@maven//:com_google_errorprone_error_prone_annotations", - ], -) - -cel_android_library( - name = "lite_program_impl_android", - srcs = LITE_PROGRAM_IMPL_SOURCES, - deps = [ - ":activation_android", - ":evaluation_exception", - ":function_resolver_android", - ":interpretable_android", - ":program_android", - ":variable_resolver", - "//:auto_value", "@maven//:com_google_errorprone_error_prone_annotations", + "@maven//:com_google_guava_guava", ], ) @@ -933,20 +920,23 @@ cel_android_library( tags = [ ], deps = [ - ":cel_value_runtime_type_provider_android", ":dispatcher_android", + ":evaluation_exception", ":function_binding_android", - ":interpreter_android", - ":lite_program_impl_android", ":lite_runtime_android", ":program_android", ":runtime_equality_android", ":runtime_helpers_android", - ":type_resolver_android", "//:auto_value", "//common:cel_ast_android", + "//common:container_android", "//common:options", + "//common/types:default_type_provider_android", + "//common/types:type_providers_android", + "//common/values:cel_value_android", "//common/values:cel_value_provider_android", + "//common/values:values_android", + "//runtime/planner:program_planner_android", "//runtime/standard:standard_function_android", "@maven//:com_google_code_findbugs_annotations", "@maven//:com_google_errorprone_error_prone_annotations", @@ -1039,46 +1029,6 @@ cel_android_library( ], ) -java_library( - name = "cel_value_runtime_type_provider", - srcs = ["CelValueRuntimeTypeProvider.java"], - deps = [ - ":runtime_type_provider", - ":unknown_attributes", - "//common/annotations", - "//common/exceptions:attribute_not_found", - "//common/values", - "//common/values:base_proto_cel_value_converter", - "//common/values:base_proto_message_value_provider", - "//common/values:cel_value", - "//common/values:cel_value_provider", - "//common/values:combined_cel_value_provider", - "@maven//:com_google_errorprone_error_prone_annotations", - "@maven//:com_google_guava_guava", - "@maven//:com_google_protobuf_protobuf_java", - ], -) - -cel_android_library( - name = "cel_value_runtime_type_provider_android", - srcs = ["CelValueRuntimeTypeProvider.java"], - deps = [ - ":runtime_type_provider_android", - ":unknown_attributes_android", - "//common/annotations", - "//common/exceptions:attribute_not_found", - "//common/values:base_proto_cel_value_converter_android", - "//common/values:base_proto_message_value_provider_android", - "//common/values:cel_value_android", - "//common/values:cel_value_provider_android", - "//common/values:combined_cel_value_provider_android", - "//common/values:values_android", - "@maven//:com_google_errorprone_error_prone_annotations", - "@maven_android//:com_google_guava_guava", - "@maven_android//:com_google_protobuf_protobuf_javalite", - ], -) - java_library( name = "interpreter_util", srcs = ["InterpreterUtil.java"], @@ -1142,8 +1092,10 @@ cel_android_library( ":program_android", "//:auto_value", "//common:cel_ast_android", + "//common:container_android", "//common:options", "//common/annotations", + "//common/types:type_providers_android", "//common/values:cel_value_provider_android", "//runtime/standard:standard_function_android", "@maven//:com_google_code_findbugs_annotations", @@ -1209,19 +1161,6 @@ cel_android_library( ], ) -java_library( - name = "variable_resolver", - srcs = [ - "CelVariableResolver.java", - "HierarchicalVariableResolver.java", - ], - # used_by_android - tags = [ - ], - deps = [ - ], -) - java_library( name = "program", srcs = ["Program.java"], @@ -1231,6 +1170,7 @@ java_library( ":evaluation_exception", ":function_resolver", ":variable_resolver", + "//runtime:variable_resolver", "@maven//:com_google_errorprone_error_prone_annotations", ], ) @@ -1274,3 +1214,14 @@ cel_android_library( "@maven_android//:com_google_guava_guava", ], ) + +java_library( + name = "variable_resolver", + srcs = [ + "CelVariableResolver.java", + "HierarchicalVariableResolver.java", + ], + # used_by_android + tags = [ + ], +) diff --git a/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java b/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java index c1f4b236f..c929b96e9 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java +++ b/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java @@ -35,6 +35,10 @@ private CelLateFunctionBindings(ImmutableMap functi this.functions = functions; } + public boolean containsFunction(String functionName) { + return functions.containsKey(functionName); + } + @Override public Optional findOverloadMatchingArgs( String functionName, Collection overloadIds, Object[] args) diff --git a/runtime/src/main/java/dev/cel/runtime/CelLiteRuntimeBuilder.java b/runtime/src/main/java/dev/cel/runtime/CelLiteRuntimeBuilder.java index 48b51274d..c7b1e63a4 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelLiteRuntimeBuilder.java +++ b/runtime/src/main/java/dev/cel/runtime/CelLiteRuntimeBuilder.java @@ -16,7 +16,9 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.CheckReturnValue; +import dev.cel.common.CelContainer; import dev.cel.common.CelOptions; +import dev.cel.common.types.CelTypeProvider; import dev.cel.common.values.CelValueProvider; import dev.cel.runtime.standard.CelStandardFunction; @@ -47,6 +49,25 @@ CelLiteRuntimeBuilder setStandardFunctions( @CanIgnoreReturnValue CelLiteRuntimeBuilder addFunctionBindings(Iterable bindings); + /** + * Adds bindings for functions that are allowed to be late-bound (resolved at execution time). + */ + @CanIgnoreReturnValue + CelLiteRuntimeBuilder addLateBoundFunctions(String... lateBoundFunctionNames); + + /** + * Adds bindings for functions that are allowed to be late-bound (resolved at execution time). + */ + @CanIgnoreReturnValue + CelLiteRuntimeBuilder addLateBoundFunctions(Iterable lateBoundFunctionNames); + + /** + * Sets the {@link CelTypeProvider} for resolving CEL types during evaluation, such as a fully + * qualified type name to a struct or an enum value. + */ + @CanIgnoreReturnValue + CelLiteRuntimeBuilder setTypeProvider(CelTypeProvider celTypeProvider); + /** * Sets the {@link CelValueProvider} for resolving struct values during evaluation. Multiple * providers can be combined using {@code CombinedCelValueProvider}. Note that if you intend to @@ -62,6 +83,13 @@ CelLiteRuntimeBuilder setStandardFunctions( @CanIgnoreReturnValue CelLiteRuntimeBuilder addLibraries(Iterable libraries); + /** + * Set the {@link CelContainer} to use as the namespace for resolving CEL expression variables and + * functions. + */ + @CanIgnoreReturnValue + CelLiteRuntimeBuilder setContainer(CelContainer container); + @CheckReturnValue CelLiteRuntime build(); } diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntimeBuilder.java b/runtime/src/main/java/dev/cel/runtime/CelRuntimeBuilder.java index e1e3c1b51..01057e140 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelRuntimeBuilder.java +++ b/runtime/src/main/java/dev/cel/runtime/CelRuntimeBuilder.java @@ -22,6 +22,7 @@ import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.Message; import dev.cel.common.CelOptions; +import dev.cel.common.types.CelTypeProvider; import dev.cel.common.values.CelValueProvider; import java.util.function.Function; @@ -48,6 +49,14 @@ public interface CelRuntimeBuilder { @CanIgnoreReturnValue CelRuntimeBuilder addFunctionBindings(Iterable bindings); + /** Adds bindings for functions that are allowed to be late-bound (resolved at execution time). */ + @CanIgnoreReturnValue + CelRuntimeBuilder addLateBoundFunctions(String... lateBoundFunctionNames); + + /** Adds bindings for functions that are allowed to be late-bound (resolved at execution time). */ + @CanIgnoreReturnValue + CelRuntimeBuilder addLateBoundFunctions(Iterable lateBoundFunctionNames); + /** * Add message {@link Descriptor}s to the builder for type-checking and object creation at * interpretation time. @@ -123,6 +132,13 @@ public interface CelRuntimeBuilder { @CanIgnoreReturnValue CelRuntimeBuilder addFileTypes(FileDescriptorSet fileDescriptorSet); + /** + * Sets the {@link CelTypeProvider} for resolving CEL types during evaluation, such as a fully + * qualified type name to a struct or an enum value. + */ + @CanIgnoreReturnValue + CelRuntimeBuilder setTypeProvider(CelTypeProvider celTypeProvider); + /** * Set a custom type factory for the runtime. * @@ -145,7 +161,7 @@ public interface CelRuntimeBuilder { * support proto messages in addition to custom struct values, protobuf value provider must be * configured first before the custom value provider. * - *

Note {@link CelOptions#enableCelValue()} must be enabled or this method will be a no-op. + *

Note that this option is only supported for planner-based runtime. */ @CanIgnoreReturnValue CelRuntimeBuilder setValueProvider(CelValueProvider celValueProvider); diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntimeFactory.java b/runtime/src/main/java/dev/cel/runtime/CelRuntimeFactory.java index e19f9d765..61f29a790 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelRuntimeFactory.java +++ b/runtime/src/main/java/dev/cel/runtime/CelRuntimeFactory.java @@ -15,6 +15,7 @@ package dev.cel.runtime; import dev.cel.common.CelOptions; +import dev.cel.common.annotations.Beta; /** Helper class to construct new {@code CelRuntime} instances. */ public final class CelRuntimeFactory { @@ -31,5 +32,16 @@ public static CelRuntimeBuilder standardCelRuntimeBuilder() { .setStandardEnvironmentEnabled(true); } + @Beta + public static CelRuntimeBuilder plannerCelRuntimeBuilder() { + return CelRuntimeImpl.newBuilder() + .setStandardFunctions(CelStandardFunctions.newBuilder().build()) + .setOptions( + CelOptions.current() + .enableTimestampEpoch(true) + .enableHeterogeneousNumericComparisons(true) + .build()); + } + private CelRuntimeFactory() {} } diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java b/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java new file mode 100644 index 000000000..7737788df --- /dev/null +++ b/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java @@ -0,0 +1,418 @@ +// Copyright 2025 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 static com.google.common.base.Preconditions.checkNotNull; + +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.Immutable; +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.Descriptors; +import com.google.protobuf.ExtensionRegistry; +import com.google.protobuf.Message; +import dev.cel.common.CelAbstractSyntaxTree; +import dev.cel.common.CelContainer; +import dev.cel.common.CelDescriptorUtil; +import dev.cel.common.CelDescriptors; +import dev.cel.common.CelOptions; +import dev.cel.common.annotations.Internal; +import dev.cel.common.internal.CelDescriptorPool; +import dev.cel.common.internal.DefaultDescriptorPool; +import dev.cel.common.internal.DefaultMessageFactory; +import dev.cel.common.internal.DynamicProto; +import dev.cel.common.types.CelTypeProvider; +import dev.cel.common.types.DefaultTypeProvider; +import dev.cel.common.types.ProtoMessageTypeProvider; +import dev.cel.common.values.CelValueConverter; +import dev.cel.common.values.CelValueProvider; +import dev.cel.common.values.CombinedCelValueProvider; +import dev.cel.common.values.ProtoMessageValueProvider; +import dev.cel.runtime.planner.ProgramPlanner; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import org.jspecify.annotations.Nullable; + +@AutoValue +@Internal +@Immutable +abstract class CelRuntimeImpl implements CelRuntime { + + abstract ProgramPlanner planner(); + + abstract CelOptions options(); + + abstract ImmutableMap functionBindings(); + + abstract ImmutableSet fileDescriptors(); + + // Callers must guarantee that a custom runtime library is immutable. CEL provided ones are + // immutable by default. + @SuppressWarnings("Immutable") + @AutoValue.CopyAnnotations + abstract ImmutableSet runtimeLibraries(); + + abstract ImmutableSet lateBoundFunctionNames(); + + abstract CelStandardFunctions standardFunctions(); + + abstract @Nullable CelTypeProvider typeProvider(); + + abstract @Nullable CelValueProvider valueProvider(); + + // Extension registry is unmodifiable. Just not marked as such from Protobuf's implementation. + @SuppressWarnings("Immutable") + @AutoValue.CopyAnnotations + abstract @Nullable ExtensionRegistry extensionRegistry(); + + public Program createProgram(CelAbstractSyntaxTree ast) throws CelEvaluationException { + return toRuntimeProgram(planner().plan(ast)); + } + + public Program toRuntimeProgram(dev.cel.runtime.Program program) { + return new Program() { + + @Override + public Object eval() throws CelEvaluationException { + return program.eval(); + } + + @Override + public Object eval(Map mapValue) throws CelEvaluationException { + return program.eval(mapValue); + } + + @Override + public Object eval(Map mapValue, CelFunctionResolver lateBoundFunctionResolver) + throws CelEvaluationException { + return program.eval(mapValue, lateBoundFunctionResolver); + } + + @Override + public Object eval(Message message) throws CelEvaluationException { + throw new UnsupportedOperationException("Not yet supported."); + } + + @Override + public Object eval(CelVariableResolver resolver) throws CelEvaluationException { + return program.eval(resolver); + } + + @Override + public Object eval( + CelVariableResolver resolver, CelFunctionResolver lateBoundFunctionResolver) + throws CelEvaluationException { + return program.eval(resolver, lateBoundFunctionResolver); + } + + @Override + public Object trace(CelEvaluationListener listener) throws CelEvaluationException { + throw new UnsupportedOperationException("Trace is not yet supported."); + } + + @Override + public Object trace(Map mapValue, CelEvaluationListener listener) + throws CelEvaluationException { + throw new UnsupportedOperationException("Trace is not yet supported."); + } + + @Override + public Object trace(Message message, CelEvaluationListener listener) + throws CelEvaluationException { + throw new UnsupportedOperationException("Trace is not yet supported."); + } + + @Override + public Object trace(CelVariableResolver resolver, CelEvaluationListener listener) + throws CelEvaluationException { + throw new UnsupportedOperationException("Trace is not yet supported."); + } + + @Override + public Object trace( + CelVariableResolver resolver, + CelFunctionResolver lateBoundFunctionResolver, + CelEvaluationListener listener) + throws CelEvaluationException { + throw new UnsupportedOperationException("Trace is not yet supported."); + } + + @Override + public Object trace( + Map mapValue, + CelFunctionResolver lateBoundFunctionResolver, + CelEvaluationListener listener) + throws CelEvaluationException { + throw new UnsupportedOperationException("Trace is not yet supported."); + } + + @Override + public Object advanceEvaluation(UnknownContext context) throws CelEvaluationException { + throw new UnsupportedOperationException("Unsupported operation."); + } + }; + } + + public abstract Builder toRuntimeBuilder(); + + static Builder newBuilder() { + return new AutoValue_CelRuntimeImpl.Builder(); + } + + @AutoValue.Builder + abstract static class Builder implements CelRuntimeBuilder { + + public abstract Builder setPlanner(ProgramPlanner planner); + + public abstract Builder setOptions(CelOptions options); + + public abstract Builder setStandardFunctions(CelStandardFunctions standardFunctions); + + public abstract Builder setExtensionRegistry(ExtensionRegistry extensionRegistry); + + public abstract Builder setTypeProvider(CelTypeProvider celTypeProvider); + + public abstract Builder setValueProvider(CelValueProvider celValueProvider); + + abstract CelOptions options(); + + abstract CelTypeProvider typeProvider(); + + abstract CelValueProvider valueProvider(); + + abstract CelStandardFunctions standardFunctions(); + + abstract ImmutableSet.Builder fileDescriptorsBuilder(); + + abstract ImmutableSet.Builder runtimeLibrariesBuilder(); + + abstract ImmutableSet.Builder lateBoundFunctionNamesBuilder(); + + private final Map mutableFunctionBindings = new HashMap<>(); + + @CanIgnoreReturnValue + public Builder addFunctionBindings(CelFunctionBinding... bindings) { + checkNotNull(bindings); + return addFunctionBindings(Arrays.asList(bindings)); + } + + @CanIgnoreReturnValue + public Builder addFunctionBindings(Iterable bindings) { + checkNotNull(bindings); + bindings.forEach(o -> mutableFunctionBindings.putIfAbsent(o.getOverloadId(), o)); + return this; + } + + @CanIgnoreReturnValue + public Builder addLateBoundFunctions(String... lateBoundFunctionNames) { + checkNotNull(lateBoundFunctionNames); + return addLateBoundFunctions(Arrays.asList(lateBoundFunctionNames)); + } + + @CanIgnoreReturnValue + public Builder addLateBoundFunctions(Iterable lateBoundFunctionNames) { + checkNotNull(lateBoundFunctionNames); + this.lateBoundFunctionNamesBuilder().addAll(lateBoundFunctionNames); + return this; + } + + @CanIgnoreReturnValue + public Builder addMessageTypes(Descriptors.Descriptor... descriptors) { + checkNotNull(descriptors); + return addMessageTypes(Arrays.asList(descriptors)); + } + + @CanIgnoreReturnValue + public Builder addMessageTypes(Iterable descriptors) { + checkNotNull(descriptors); + return addFileTypes(CelDescriptorUtil.getFileDescriptorsForDescriptors(descriptors)); + } + + @CanIgnoreReturnValue + public Builder addFileTypes(DescriptorProtos.FileDescriptorSet fileDescriptorSet) { + checkNotNull(fileDescriptorSet); + return addFileTypes( + CelDescriptorUtil.getFileDescriptorsFromFileDescriptorSet(fileDescriptorSet)); + } + + @CanIgnoreReturnValue + public Builder addFileTypes(Descriptors.FileDescriptor... fileDescriptors) { + checkNotNull(fileDescriptors); + return addFileTypes(Arrays.asList(fileDescriptors)); + } + + @CanIgnoreReturnValue + public Builder addFileTypes(Iterable fileDescriptors) { + checkNotNull(fileDescriptors); + this.fileDescriptorsBuilder().addAll(fileDescriptors); + return this; + } + + @CanIgnoreReturnValue + public Builder addLibraries(CelRuntimeLibrary... libraries) { + checkNotNull(libraries); + return this.addLibraries(Arrays.asList(libraries)); + } + + @CanIgnoreReturnValue + public Builder addLibraries(Iterable libraries) { + checkNotNull(libraries); + this.runtimeLibrariesBuilder().addAll(libraries); + return this; + } + + abstract Builder setFunctionBindings(ImmutableMap value); + + public Builder setTypeFactory(Function typeFactory) { + throw new UnsupportedOperationException("Unsupported. Use a custom value provider instead."); + } + + public Builder setStandardEnvironmentEnabled(boolean value) { + throw new UnsupportedOperationException( + "Unsupported. Subset the environment using setStandardFunctions instead."); + } + + /** Throws if an unsupported flag in CelOptions is toggled. */ + private static void assertAllowedCelOptions(CelOptions celOptions) { + String prefix = "Misconfigured CelOptions: "; + if (!celOptions.enableUnsignedLongs()) { + throw new IllegalArgumentException(prefix + "enableUnsignedLongs cannot be disabled."); + } + if (!celOptions.unwrapWellKnownTypesOnFunctionDispatch()) { + throw new IllegalArgumentException( + prefix + "unwrapWellKnownTypesOnFunctionDispatch cannot be disabled."); + } + + // Disallowed options in favor of subsetting + String subsettingError = "Subset the environment instead using setStandardFunctions method."; + if (!celOptions.enableStringConcatenation()) { + throw new IllegalArgumentException( + prefix + "enableStringConcatenation cannot be disabled. " + subsettingError); + } + + if (!celOptions.enableStringConversion()) { + throw new IllegalArgumentException( + prefix + "enableStringConversion cannot be disabled. " + subsettingError); + } + + if (!celOptions.enableListConcatenation()) { + throw new IllegalArgumentException( + prefix + "enableListConcatenation cannot be disabled. " + subsettingError); + } + + if (!celOptions.enableTimestampEpoch()) { + throw new IllegalArgumentException( + prefix + "enableTimestampEpoch cannot be disabled. " + subsettingError); + } + + if (!celOptions.enableHeterogeneousNumericComparisons()) { + throw new IllegalArgumentException( + prefix + + "enableHeterogeneousNumericComparisons cannot be disabled. " + + subsettingError); + } + } + + abstract CelRuntimeImpl autoBuild(); + + private static DefaultDispatcher newDispatcher( + CelStandardFunctions standardFunctions, + Collection customFunctionBindings, + RuntimeEquality runtimeEquality, + CelOptions options) { + DefaultDispatcher.Builder builder = DefaultDispatcher.newBuilder(); + for (CelFunctionBinding binding : + standardFunctions.newFunctionBindings(runtimeEquality, options)) { + builder.addOverload( + binding.getOverloadId(), + binding.getArgTypes(), + binding.isStrict(), + binding.getDefinition()); + } + + for (CelFunctionBinding binding : customFunctionBindings) { + builder.addOverload( + binding.getOverloadId(), + binding.getArgTypes(), + binding.isStrict(), + binding.getDefinition()); + } + + return builder.build(); + } + + public CelRuntime build() { + assertAllowedCelOptions(options()); + CelDescriptors celDescriptors = + CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(fileDescriptorsBuilder().build()); + + CelDescriptorPool descriptorPool = DefaultDescriptorPool.create(celDescriptors); + DefaultMessageFactory defaultMessageFactory = DefaultMessageFactory.create(descriptorPool); + DynamicProto dynamicProto = DynamicProto.create(defaultMessageFactory); + CelValueProvider protoMessageValueProvider = + ProtoMessageValueProvider.newInstance(options(), dynamicProto); + CelValueConverter celValueConverter = protoMessageValueProvider.celValueConverter(); + if (valueProvider() != null) { + protoMessageValueProvider = + CombinedCelValueProvider.combine(protoMessageValueProvider, valueProvider()); + } + + RuntimeEquality runtimeEquality = + RuntimeEquality.create( + ProtoMessageRuntimeHelpers.create(dynamicProto, options()), options()); + ImmutableSet runtimeLibraries = runtimeLibrariesBuilder().build(); + // Add libraries, such as extensions + for (CelRuntimeLibrary celLibrary : runtimeLibraries) { + if (celLibrary instanceof CelInternalRuntimeLibrary) { + ((CelInternalRuntimeLibrary) celLibrary) + .setRuntimeOptions(this, runtimeEquality, options()); + } else { + celLibrary.setRuntimeOptions(this); + } + } + + CelTypeProvider combinedTypeProvider = + new CelTypeProvider.CombinedCelTypeProvider( + new ProtoMessageTypeProvider(celDescriptors), DefaultTypeProvider.getInstance()); + if (typeProvider() != null) { + combinedTypeProvider = + new CelTypeProvider.CombinedCelTypeProvider(combinedTypeProvider, typeProvider()); + } + + DefaultDispatcher dispatcher = + newDispatcher( + standardFunctions(), mutableFunctionBindings.values(), runtimeEquality, options()); + + ProgramPlanner planner = + ProgramPlanner.newPlanner( + combinedTypeProvider, + protoMessageValueProvider, + dispatcher, + celValueConverter, + CelContainer.newBuilder().build(), // TODO: Accept CEL container + options(), + lateBoundFunctionNamesBuilder().build()); + setPlanner(planner); + + setFunctionBindings(ImmutableMap.copyOf(mutableFunctionBindings)); + return autoBuild(); + } + } +} diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java b/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java index 15591e680..23cfb5dcf 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java +++ b/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java @@ -40,9 +40,10 @@ import dev.cel.common.internal.DynamicProto; // CEL-Internal-3 import dev.cel.common.internal.ProtoMessageFactory; +import dev.cel.common.types.CelTypeProvider; import dev.cel.common.types.CelTypes; import dev.cel.common.values.CelValueProvider; -import dev.cel.common.values.ProtoMessageValueProvider; +import dev.cel.runtime.FunctionBindingImpl.DynamicDispatchBinding; import dev.cel.runtime.standard.AddOperator.AddOverload; import dev.cel.runtime.standard.IntFunction.IntOverload; import dev.cel.runtime.standard.TimestampFunction.TimestampOverload; @@ -77,7 +78,6 @@ public final class CelRuntimeLegacyImpl implements CelRuntime { private final Function customTypeFactory; private final CelStandardFunctions overriddenStandardFunctions; - private final CelValueProvider celValueProvider; private final ImmutableSet fileDescriptors; // This does not affect the evaluation behavior in any manner. @@ -111,10 +111,6 @@ public CelRuntimeBuilder toRuntimeBuilder() { builder.setStandardFunctions(overriddenStandardFunctions); } - if (celValueProvider != null) { - builder.setValueProvider(celValueProvider); - } - return builder; } @@ -134,7 +130,6 @@ public static final class Builder implements CelRuntimeBuilder { @VisibleForTesting final ImmutableSet.Builder celRuntimeLibraries; @VisibleForTesting Function customTypeFactory; - @VisibleForTesting CelValueProvider celValueProvider; @VisibleForTesting CelStandardFunctions overriddenStandardFunctions; private CelOptions options; @@ -160,6 +155,18 @@ public CelRuntimeBuilder addFunctionBindings(Iterable bindin return this; } + @Override + public CelRuntimeBuilder addLateBoundFunctions(String... lateBoundFunctionNames) { + throw new UnsupportedOperationException( + "This method is not supported for the legacy runtime"); + } + + @Override + public CelRuntimeBuilder addLateBoundFunctions(Iterable lateBoundFunctionNames) { + throw new UnsupportedOperationException( + "This method is not supported for the legacy runtime"); + } + @Override public CelRuntimeBuilder addMessageTypes(Descriptor... descriptors) { return addMessageTypes(Arrays.asList(descriptors)); @@ -188,14 +195,20 @@ public CelRuntimeBuilder addFileTypes(FileDescriptorSet fileDescriptorSet) { } @Override - public CelRuntimeBuilder setTypeFactory(Function typeFactory) { - this.customTypeFactory = typeFactory; - return this; + public CelRuntimeBuilder setTypeProvider(CelTypeProvider celTypeProvider) { + throw new UnsupportedOperationException( + "setTypeProvider is not supported for legacy runtime"); } @Override public CelRuntimeBuilder setValueProvider(CelValueProvider celValueProvider) { - this.celValueProvider = celValueProvider; + throw new UnsupportedOperationException( + "setValueProvider is not supported for legacy runtime"); + } + + @Override + public CelRuntimeBuilder setTypeFactory(Function typeFactory) { + this.customTypeFactory = typeFactory; return this; } @@ -281,6 +294,10 @@ public CelRuntimeLegacyImpl build() { ImmutableMap.builder(); for (CelFunctionBinding standardFunctionBinding : newStandardFunctionBindings(runtimeEquality)) { + if (standardFunctionBinding instanceof DynamicDispatchBinding) { + continue; + } + functionBindingsBuilder.put( standardFunctionBinding.getOverloadId(), standardFunctionBinding); } @@ -295,19 +312,8 @@ public CelRuntimeLegacyImpl build() { dispatcherBuilder.addOverload( overloadId, func.getArgTypes(), func.isStrict(), func.getDefinition())); - RuntimeTypeProvider runtimeTypeProvider; - - if (options.enableCelValue()) { - CelValueProvider messageValueProvider = celValueProvider; - - if (messageValueProvider == null) { - messageValueProvider = ProtoMessageValueProvider.newInstance(options, dynamicProto); - } - - runtimeTypeProvider = CelValueRuntimeTypeProvider.newInstance(messageValueProvider); - } else { - runtimeTypeProvider = new DescriptorMessageProvider(runtimeTypeFactory, options); - } + RuntimeTypeProvider runtimeTypeProvider = + new DescriptorMessageProvider(runtimeTypeFactory, options); DefaultInterpreter interpreter = new DefaultInterpreter( @@ -323,7 +329,6 @@ public CelRuntimeLegacyImpl build() { extensionRegistry, customTypeFactory, overriddenStandardFunctions, - celValueProvider, fileDescriptors, runtimeLibraries, ImmutableList.copyOf(customFunctionBindings.values())); @@ -366,7 +371,8 @@ private ImmutableSet newStandardFunctionBindings( break; default: if (!options.enableHeterogeneousNumericComparisons()) { - return !CelStandardFunctions.isHeterogeneousComparison(standardOverload); + return !CelStandardFunctions.isHeterogeneousComparison( + standardOverload); } break; } @@ -420,7 +426,6 @@ private CelRuntimeLegacyImpl( ExtensionRegistry extensionRegistry, @Nullable Function customTypeFactory, @Nullable CelStandardFunctions overriddenStandardFunctions, - @Nullable CelValueProvider celValueProvider, ImmutableSet fileDescriptors, ImmutableSet celRuntimeLibraries, ImmutableList celFunctionBindings) { @@ -430,7 +435,6 @@ private CelRuntimeLegacyImpl( this.extensionRegistry = extensionRegistry; this.customTypeFactory = customTypeFactory; this.overriddenStandardFunctions = overriddenStandardFunctions; - this.celValueProvider = celValueProvider; this.fileDescriptors = fileDescriptors; this.celRuntimeLibraries = celRuntimeLibraries; this.celFunctionBindings = celFunctionBindings; diff --git a/runtime/src/main/java/dev/cel/runtime/CelValueRuntimeTypeProvider.java b/runtime/src/main/java/dev/cel/runtime/CelValueRuntimeTypeProvider.java deleted file mode 100644 index e071289ca..000000000 --- a/runtime/src/main/java/dev/cel/runtime/CelValueRuntimeTypeProvider.java +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2023 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 static com.google.common.base.Preconditions.checkNotNull; - -import com.google.errorprone.annotations.Immutable; -import com.google.protobuf.MessageLite; -import dev.cel.common.annotations.Internal; -import dev.cel.common.exceptions.CelAttributeNotFoundException; -import dev.cel.common.values.BaseProtoCelValueConverter; -import dev.cel.common.values.BaseProtoMessageValueProvider; -import dev.cel.common.values.CelValue; -import dev.cel.common.values.CelValueProvider; -import dev.cel.common.values.CombinedCelValueProvider; -import dev.cel.common.values.SelectableValue; -import java.util.Map; -import java.util.NoSuchElementException; - -/** Bridge between the old RuntimeTypeProvider and CelValueProvider APIs. */ -@Internal -@Immutable -final class CelValueRuntimeTypeProvider implements RuntimeTypeProvider { - - private final CelValueProvider valueProvider; - private final BaseProtoCelValueConverter protoCelValueConverter; - private static final BaseProtoCelValueConverter DEFAULT_CEL_VALUE_CONVERTER = - new BaseProtoCelValueConverter() {}; - - static CelValueRuntimeTypeProvider newInstance(CelValueProvider valueProvider) { - BaseProtoCelValueConverter converter = DEFAULT_CEL_VALUE_CONVERTER; - - // Find the underlying ProtoCelValueConverter. - // This is required because DefaultInterpreter works with a resolved protobuf messages directly - // in evaluation flow. - // A new runtime should not directly depend on protobuf, thus this will not be needed in the - // future. - if (valueProvider instanceof BaseProtoMessageValueProvider) { - converter = ((BaseProtoMessageValueProvider) valueProvider).protoCelValueConverter(); - } else if (valueProvider instanceof CombinedCelValueProvider) { - converter = - ((CombinedCelValueProvider) valueProvider) - .valueProviders().stream() - .filter(p -> p instanceof BaseProtoMessageValueProvider) - .map(p -> ((BaseProtoMessageValueProvider) p).protoCelValueConverter()) - .findFirst() - .orElse(DEFAULT_CEL_VALUE_CONVERTER); - } - - return new CelValueRuntimeTypeProvider(valueProvider, converter); - } - - @Override - public Object createMessage(String messageName, Map values) { - return maybeUnwrapCelValue( - valueProvider - .newValue(messageName, values) - .orElseThrow( - () -> - new NoSuchElementException( - String.format("cannot resolve '%s' as a message", messageName)))); - } - - @Override - public Object selectField(Object message, String fieldName) { - if (message instanceof Map) { - Map map = (Map) message; - if (map.containsKey(fieldName)) { - return map.get(fieldName); - } - - throw CelAttributeNotFoundException.forMissingMapKey(fieldName); - } - - SelectableValue selectableValue = getSelectableValueOrThrow(message, fieldName); - Object value = selectableValue.select(fieldName); - - return maybeUnwrapCelValue(value); - } - - @Override - public Object hasField(Object message, String fieldName) { - SelectableValue selectableValue = getSelectableValueOrThrow(message, fieldName); - - return selectableValue.find(fieldName).isPresent(); - } - - @SuppressWarnings("unchecked") - private SelectableValue getSelectableValueOrThrow(Object obj, String fieldName) { - Object convertedCelValue = protoCelValueConverter.toRuntimeValue(obj); - - if (!(convertedCelValue instanceof SelectableValue)) { - throwInvalidFieldSelection(fieldName); - } - - return (SelectableValue) convertedCelValue; - } - - @Override - public Object adapt(String messageName, Object message) { - if (message instanceof CelUnknownSet) { - return message; // CelUnknownSet is handled specially for iterative evaluation. No need to - // adapt to CelValue. - } - - if (message instanceof MessageLite.Builder) { - message = ((MessageLite.Builder) message).build(); - } - - if (message instanceof MessageLite) { - return maybeUnwrapCelValue(protoCelValueConverter.toRuntimeValue(message)); - } - - return message; - } - - /** - * DefaultInterpreter cannot handle CelValue and instead expects plain Java objects. - * - *

This will become unnecessary once we introduce a rewrite of a Cel runtime. - */ - private Object maybeUnwrapCelValue(Object object) { - if (object instanceof CelValue) { - return protoCelValueConverter.unwrap((CelValue) object); - } - return object; - } - - private static void throwInvalidFieldSelection(String fieldName) { - throw CelAttributeNotFoundException.forFieldResolution(fieldName); - } - - private CelValueRuntimeTypeProvider( - CelValueProvider valueProvider, BaseProtoCelValueConverter protoCelValueConverter) { - this.valueProvider = checkNotNull(valueProvider); - this.protoCelValueConverter = checkNotNull(protoCelValueConverter); - } -} diff --git a/runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java b/runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java index ba5d05915..e49658190 100644 --- a/runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java +++ b/runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java @@ -864,7 +864,7 @@ private IntermediateResult evalMap(ExecutionFrame frame, CelMap mapExpr) throw CelEvaluationExceptionBuilder.newBuilder( "duplicate map key [%s]", keyResult.value()) .setErrorCode(CelErrorCode.DUPLICATE_ATTRIBUTE) - .setMetadata(metadata, entry.id()) + .setMetadata(metadata, entry.key().id()) .build(); } @@ -967,7 +967,7 @@ private IntermediateResult maybeAdaptViewToList(IntermediateResult accuValue) { @SuppressWarnings("unchecked") private IntermediateResult evalComprehension( - ExecutionFrame frame, CelExpr unusedExpr, CelComprehension compre) + ExecutionFrame frame, CelExpr compreExpr, CelComprehension compre) throws CelEvaluationException { String accuVar = compre.accuVar(); String iterVar = compre.iterVar(); @@ -1000,7 +1000,7 @@ private IntermediateResult evalComprehension( } int i = 0; for (Object elem : iterRange) { - frame.incrementIterations(); + frame.incrementIterations(metadata, compreExpr.id()); CelAttribute iterAttr = CelAttribute.EMPTY; if (iterRange instanceof List) { @@ -1183,13 +1183,14 @@ private Optional findOverload( return Optional.empty(); } - private void incrementIterations() throws CelEvaluationException { + private void incrementIterations(Metadata metadata, long exprId) throws CelEvaluationException { if (maxIterations < 0) { return; } if (++iterations > maxIterations) { throw CelEvaluationExceptionBuilder.newBuilder( String.format("Iteration budget exceeded: %d", maxIterations)) + .setMetadata(metadata, exprId) .setErrorCode(CelErrorCode.ITERATION_BUDGET_EXCEEDED) .build(); } diff --git a/runtime/src/main/java/dev/cel/runtime/LiteProgramImpl.java b/runtime/src/main/java/dev/cel/runtime/LiteProgramImpl.java deleted file mode 100644 index 5e57f497b..000000000 --- a/runtime/src/main/java/dev/cel/runtime/LiteProgramImpl.java +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2025 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.auto.value.AutoValue; -import com.google.errorprone.annotations.Immutable; -import java.util.Map; - -@Immutable -@AutoValue -abstract class LiteProgramImpl implements Program { - - abstract Interpretable interpretable(); - - @Override - public Object eval() throws CelEvaluationException { - return interpretable().eval(GlobalResolver.EMPTY); - } - - @Override - public Object eval(Map mapValue) throws CelEvaluationException { - return interpretable().eval(Activation.copyOf(mapValue)); - } - - @Override - public Object eval(Map mapValue, CelFunctionResolver lateBoundFunctionResolver) - throws CelEvaluationException { - return interpretable().eval(Activation.copyOf(mapValue), lateBoundFunctionResolver); - } - - @Override - public Object eval(CelVariableResolver resolver, CelFunctionResolver lateBoundFunctionResolver) { - // TODO: Wire in program planner - throw new UnsupportedOperationException("To be implemented"); - } - - @Override - public Object eval(CelVariableResolver resolver) throws CelEvaluationException { - // TODO: Wire in program planner - throw new UnsupportedOperationException("To be implemented"); - } - - static Program plan(Interpretable interpretable) { - return new AutoValue_LiteProgramImpl(interpretable); - } -} diff --git a/runtime/src/main/java/dev/cel/runtime/LiteRuntimeImpl.java b/runtime/src/main/java/dev/cel/runtime/LiteRuntimeImpl.java index 45c322da9..9141b2419 100644 --- a/runtime/src/main/java/dev/cel/runtime/LiteRuntimeImpl.java +++ b/runtime/src/main/java/dev/cel/runtime/LiteRuntimeImpl.java @@ -15,7 +15,6 @@ package dev.cel.runtime; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; @@ -23,29 +22,36 @@ import com.google.common.collect.ImmutableSet; import javax.annotation.concurrent.ThreadSafe; import dev.cel.common.CelAbstractSyntaxTree; +import dev.cel.common.CelContainer; import dev.cel.common.CelOptions; +import dev.cel.common.types.CelTypeProvider; +import dev.cel.common.types.DefaultTypeProvider; +import dev.cel.common.values.CelValue; +import dev.cel.common.values.CelValueConverter; import dev.cel.common.values.CelValueProvider; +import dev.cel.runtime.planner.ProgramPlanner; import dev.cel.runtime.standard.CelStandardFunction; import java.util.Arrays; import java.util.HashMap; +import java.util.Map; import java.util.Optional; @ThreadSafe final class LiteRuntimeImpl implements CelLiteRuntime { - private final Interpreter interpreter; + private final ProgramPlanner planner; private final CelOptions celOptions; private final ImmutableList customFunctionBindings; private final ImmutableSet celStandardFunctions; + private final CelTypeProvider celTypeProvider; private final CelValueProvider celValueProvider; + private final CelContainer celContainer; - // This does not affect the evaluation behavior in any manner. // CEL-Internal-4 private final ImmutableSet runtimeLibraries; @Override - public Program createProgram(CelAbstractSyntaxTree ast) { - checkState(ast.isChecked(), "programs must be created from checked expressions"); - return LiteProgramImpl.plan(interpreter.createInterpretable(ast)); + public Program createProgram(CelAbstractSyntaxTree ast) throws CelEvaluationException { + return planner.plan(ast); } @Override @@ -55,22 +61,30 @@ public CelLiteRuntimeBuilder toRuntimeBuilder() { .setOptions(celOptions) .setStandardFunctions(celStandardFunctions) .addFunctionBindings(customFunctionBindings) - .addLibraries(runtimeLibraries); + .addLibraries(runtimeLibraries) + .setContainer(celContainer); if (celValueProvider != null) { builder.setValueProvider(celValueProvider); } + if (celTypeProvider != null) { + builder.setTypeProvider(celTypeProvider); + } + return builder; } static final class Builder implements CelLiteRuntimeBuilder { + private CelContainer container; // Following is visible to test `toBuilder`. @VisibleForTesting CelOptions celOptions; @VisibleForTesting final HashMap customFunctionBindings; + @VisibleForTesting final ImmutableSet.Builder lateBoundFunctionNamesBuilder; @VisibleForTesting final ImmutableSet.Builder runtimeLibrariesBuilder; @VisibleForTesting final ImmutableSet.Builder standardFunctionBuilder; + @VisibleForTesting CelTypeProvider celTypeProvider; @VisibleForTesting CelValueProvider celValueProvider; @Override @@ -102,6 +116,23 @@ public CelLiteRuntimeBuilder addFunctionBindings(Iterable bi return this; } + @Override + public CelLiteRuntimeBuilder addLateBoundFunctions(String... lateBoundFunctionNames) { + return addLateBoundFunctions(Arrays.asList(lateBoundFunctionNames)); + } + + @Override + public CelLiteRuntimeBuilder addLateBoundFunctions(Iterable lateBoundFunctionNames) { + lateBoundFunctionNamesBuilder.addAll(lateBoundFunctionNames); + return this; + } + + @Override + public CelLiteRuntimeBuilder setTypeProvider(CelTypeProvider celTypeProvider) { + this.celTypeProvider = celTypeProvider; + return this; + } + @Override public CelLiteRuntimeBuilder setValueProvider(CelValueProvider celValueProvider) { this.celValueProvider = celValueProvider; @@ -119,12 +150,15 @@ public CelLiteRuntimeBuilder addLibraries(Iterable Optional.empty(); + this.celOptions = CelOptions.DEFAULT; + this.celValueProvider = + new CelValueProvider() { + @Override + public Optional newValue(String structType, Map fields) { + return Optional.empty(); + } + + @Override + public CelValueConverter celValueConverter() { + return new CelValueConverter() { + @Override + public Object unwrap(CelValue celValue) { + return super.unwrap(celValue); + } + }; + } + }; this.customFunctionBindings = new HashMap<>(); this.standardFunctionBuilder = ImmutableSet.builder(); this.runtimeLibrariesBuilder = ImmutableSet.builder(); + this.lateBoundFunctionNamesBuilder = ImmutableSet.builder(); + this.container = CelContainer.newBuilder().build(); } } @@ -217,17 +277,21 @@ static CelLiteRuntimeBuilder newBuilder() { } private LiteRuntimeImpl( - Interpreter interpreter, CelOptions celOptions, Iterable customFunctionBindings, ImmutableSet celStandardFunctions, ImmutableSet runtimeLibraries, - CelValueProvider celValueProvider) { - this.interpreter = interpreter; + CelValueProvider celValueProvider, + CelTypeProvider celTypeProvider, + CelContainer celContainer, + ProgramPlanner planner) { this.celOptions = celOptions; this.customFunctionBindings = ImmutableList.copyOf(customFunctionBindings); this.celStandardFunctions = celStandardFunctions; this.runtimeLibraries = runtimeLibraries; this.celValueProvider = celValueProvider; + this.celTypeProvider = celTypeProvider; + this.celContainer = celContainer; + this.planner = planner; } } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel index 92733b1b8..2a2d449ea 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel @@ -1,4 +1,5 @@ load("@rules_java//java:defs.bzl", "java_library") +load("//:cel_android_rules.bzl", "cel_android_library") package( default_applicable_licenses = ["//:license"], @@ -57,19 +58,68 @@ java_library( ], ) +cel_android_library( + name = "program_planner_android", + srcs = ["ProgramPlanner.java"], + tags = [ + ], + deps = [ + ":attribute_android", + ":error_metadata_android", + ":eval_and_android", + ":eval_attribute_android", + ":eval_conditional_android", + ":eval_const_android", + ":eval_create_list_android", + ":eval_create_map_android", + ":eval_create_struct_android", + ":eval_fold_android", + ":eval_late_bound_call_android", + ":eval_or_android", + ":eval_test_only_android", + ":eval_unary_android", + ":eval_var_args_call_android", + ":eval_zero_arity_android", + ":interpretable_attribute_android", + ":planned_interpretable_android", + ":planned_program_android", + ":qualifier_android", + ":string_qualifier_android", + "//:auto_value", + "//common:cel_ast_android", + "//common:container_android", + "//common:operator_android", + "//common:options", + "//common/annotations", + "//common/ast:ast_android", + "//common/exceptions:overload_not_found", + "//common/types:type_providers_android", + "//common/types:types_android", + "//common/values:cel_value_provider_android", + "//common/values:values_android", + "//runtime:dispatcher_android", + "//runtime:evaluation_exception", + "//runtime:evaluation_exception_builder", + "//runtime:late_function_binding_android", + "//runtime:program_android", + "//runtime:resolved_overload_android", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "planned_program", srcs = ["PlannedProgram.java"], deps = [ ":error_metadata", ":execution_frame", + ":localized_evaluation_exception", ":planned_interpretable", - ":strict_error_exception", "//:auto_value", "//common:options", "//common/exceptions:runtime_exception", "//common/values", - "//runtime", "//runtime:activation", "//runtime:evaluation_exception", "//runtime:evaluation_exception_builder", @@ -77,6 +127,31 @@ java_library( "//runtime:interpretable", "//runtime:program", "//runtime:resolved_overload", + "//runtime:variable_resolver", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) + +cel_android_library( + name = "planned_program_android", + srcs = ["PlannedProgram.java"], + deps = [ + ":error_metadata_android", + ":execution_frame_android", + ":localized_evaluation_exception", + ":planned_interpretable_android", + "//:auto_value", + "//common:options", + "//common/exceptions:runtime_exception", + "//common/values:values_android", + "//runtime:activation_android", + "//runtime:evaluation_exception", + "//runtime:evaluation_exception_builder", + "//runtime:interpretable_android", + "//runtime:program_android", + "//runtime:resolved_overload_android", + "//runtime:variable_resolver", + "//runtime/src/main/java/dev/cel/runtime:function_resolver_android", "@maven//:com_google_errorprone_error_prone_annotations", ], ) @@ -87,14 +162,26 @@ java_library( deps = [ ":execution_frame", ":planned_interpretable", - "//common/values", - "//common/values:cel_byte_string", "//runtime:interpretable", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", ], ) +cel_android_library( + name = "eval_const_android", + srcs = ["EvalConstant.java"], + deps = [ + ":execution_frame_android", + ":planned_interpretable_android", + "//common/values:cel_byte_string", + "//common/values:values_android", + "//runtime:interpretable_android", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "interpretable_attribute", srcs = ["InterpretableAttribute.java"], @@ -105,16 +192,28 @@ java_library( ], ) +cel_android_library( + name = "interpretable_attribute_android", + srcs = ["InterpretableAttribute.java"], + deps = [ + ":planned_interpretable_android", + ":qualifier_android", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) + +ATTRIBUTE_SOURCES = [ + "Attribute.java", + "AttributeFactory.java", + "MaybeAttribute.java", + "MissingAttribute.java", + "NamespacedAttribute.java", + "RelativeAttribute.java", +] + java_library( name = "attribute", - srcs = [ - "Attribute.java", - "AttributeFactory.java", - "MaybeAttribute.java", - "MissingAttribute.java", - "NamespacedAttribute.java", - "RelativeAttribute.java", - ], + srcs = ATTRIBUTE_SOURCES, deps = [ ":eval_helpers", ":execution_frame", @@ -132,6 +231,26 @@ java_library( ], ) +cel_android_library( + name = "attribute_android", + srcs = ATTRIBUTE_SOURCES, + deps = [ + ":eval_helpers_android", + ":execution_frame_android", + ":planned_interpretable_android", + ":qualifier_android", + "//common/exceptions:attribute_not_found", + "//common/src/main/java/dev/cel/common:container_android", + "//common/types:type_providers_android", + "//common/types:types_android", + "//common/values:cel_value_android", + "//common/values:values_android", + "//runtime:interpretable_android", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "qualifier", srcs = ["Qualifier.java"], @@ -140,6 +259,14 @@ java_library( ], ) +cel_android_library( + name = "qualifier_android", + srcs = ["Qualifier.java"], + deps = [ + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) + java_library( name = "presence_test_qualifier", srcs = ["PresenceTestQualifier.java"], @@ -147,6 +274,18 @@ java_library( ":attribute", ":qualifier", "//common/values", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) + +cel_android_library( + name = "presence_test_qualifier_android", + srcs = ["PresenceTestQualifier.java"], + deps = [ + ":attribute_android", + ":qualifier_android", + "//common/values:values_android", + "@maven//:com_google_errorprone_error_prone_annotations", ], ) @@ -157,6 +296,18 @@ java_library( ":qualifier", "//common/exceptions:attribute_not_found", "//common/values", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) + +cel_android_library( + name = "string_qualifier_android", + srcs = ["StringQualifier.java"], + deps = [ + ":qualifier_android", + "//common/exceptions:attribute_not_found", + "//common/values:values_android", + "@maven//:com_google_errorprone_error_prone_annotations", ], ) @@ -174,6 +325,19 @@ java_library( ], ) +cel_android_library( + name = "eval_attribute_android", + srcs = ["EvalAttribute.java"], + deps = [ + ":attribute_android", + ":execution_frame_android", + ":interpretable_attribute_android", + ":qualifier_android", + "//runtime:interpretable_android", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) + java_library( name = "eval_test_only", srcs = ["EvalTestOnly.java"], @@ -188,6 +352,20 @@ java_library( ], ) +cel_android_library( + name = "eval_test_only_android", + srcs = ["EvalTestOnly.java"], + deps = [ + ":execution_frame_android", + ":interpretable_attribute_android", + ":presence_test_qualifier_android", + ":qualifier_android", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) + java_library( name = "eval_zero_arity", srcs = ["EvalZeroArity.java"], @@ -202,6 +380,20 @@ java_library( ], ) +cel_android_library( + name = "eval_zero_arity_android", + srcs = ["EvalZeroArity.java"], + deps = [ + ":execution_frame_android", + ":planned_interpretable_android", + "//common/values:cel_value_android", + "//common/values:values_android", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "//runtime:resolved_overload_android", + ], +) + java_library( name = "eval_unary", srcs = ["EvalUnary.java"], @@ -217,6 +409,21 @@ java_library( ], ) +cel_android_library( + name = "eval_unary_android", + srcs = ["EvalUnary.java"], + deps = [ + ":eval_helpers_android", + ":execution_frame_android", + ":planned_interpretable_android", + "//common/values:cel_value_android", + "//common/values:values_android", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "//runtime:resolved_overload_android", + ], +) + java_library( name = "eval_var_args_call", srcs = ["EvalVarArgsCall.java"], @@ -232,6 +439,21 @@ java_library( ], ) +cel_android_library( + name = "eval_var_args_call_android", + srcs = ["EvalVarArgsCall.java"], + deps = [ + ":eval_helpers_android", + ":execution_frame_android", + ":planned_interpretable_android", + "//common/values:cel_value_android", + "//common/values:values_android", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "//runtime:resolved_overload_android", + ], +) + java_library( name = "eval_late_bound_call", srcs = ["EvalLateBoundCall.java"], @@ -249,6 +471,23 @@ java_library( ], ) +cel_android_library( + name = "eval_late_bound_call_android", + srcs = ["EvalLateBoundCall.java"], + deps = [ + ":eval_helpers_android", + ":execution_frame_android", + ":planned_interpretable_android", + "//common/exceptions:overload_not_found", + "//common/values:cel_value_android", + "//common/values:values_android", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "//runtime:resolved_overload_android", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "eval_or", srcs = ["EvalOr.java"], @@ -262,6 +501,19 @@ java_library( ], ) +cel_android_library( + name = "eval_or_android", + srcs = ["EvalOr.java"], + deps = [ + ":eval_helpers_android", + ":execution_frame_android", + ":planned_interpretable_android", + "//common/values:values_android", + "//runtime:interpretable_android", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "eval_and", srcs = ["EvalAnd.java"], @@ -275,6 +527,19 @@ java_library( ], ) +cel_android_library( + name = "eval_and_android", + srcs = ["EvalAnd.java"], + deps = [ + ":eval_helpers_android", + ":execution_frame_android", + ":planned_interpretable_android", + "//common/values:values_android", + "//runtime:interpretable_android", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "eval_conditional", srcs = ["EvalConditional.java"], @@ -287,6 +552,18 @@ java_library( ], ) +cel_android_library( + name = "eval_conditional_android", + srcs = ["EvalConditional.java"], + deps = [ + ":execution_frame_android", + ":planned_interpretable_android", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "eval_create_struct", srcs = ["EvalCreateStruct.java"], @@ -303,10 +580,26 @@ java_library( ], ) +cel_android_library( + name = "eval_create_struct_android", + srcs = ["EvalCreateStruct.java"], + deps = [ + ":execution_frame_android", + ":planned_interpretable_android", + "//common/types:types_android", + "//common/values:cel_value_provider_android", + "//common/values:values_android", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) + java_library( name = "eval_create_list", srcs = ["EvalCreateList.java"], deps = [ + ":eval_helpers", ":execution_frame", ":planned_interpretable", "//runtime:evaluation_exception", @@ -316,12 +609,28 @@ java_library( ], ) +cel_android_library( + name = "eval_create_list_android", + srcs = ["EvalCreateList.java"], + deps = [ + ":eval_helpers_android", + ":execution_frame_android", + ":planned_interpretable_android", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "eval_create_map", srcs = ["EvalCreateMap.java"], deps = [ ":execution_frame", + ":localized_evaluation_exception", ":planned_interpretable", + "//common/exceptions:duplicate_key", "//runtime:evaluation_exception", "//runtime:interpretable", "@maven//:com_google_errorprone_error_prone_annotations", @@ -329,6 +638,21 @@ java_library( ], ) +cel_android_library( + name = "eval_create_map_android", + srcs = ["EvalCreateMap.java"], + deps = [ + ":execution_frame_android", + ":localized_evaluation_exception", + ":planned_interpretable_android", + "//common/exceptions:duplicate_key", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "eval_fold", srcs = ["EvalFold.java"], @@ -344,6 +668,21 @@ java_library( ], ) +cel_android_library( + name = "eval_fold_android", + srcs = ["EvalFold.java"], + deps = [ + ":execution_frame_android", + ":planned_interpretable_android", + "//runtime:concatenated_list_view", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven//:org_jspecify_jspecify", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "execution_frame", srcs = ["ExecutionFrame.java"], @@ -356,13 +695,25 @@ java_library( ], ) +cel_android_library( + name = "execution_frame_android", + srcs = ["ExecutionFrame.java"], + deps = [ + "//common:options", + "//common/exceptions:iteration_budget_exceeded", + "//runtime:evaluation_exception", + "//runtime:resolved_overload_android", + "//runtime/src/main/java/dev/cel/runtime:function_resolver_android", + ], +) + java_library( name = "eval_helpers", srcs = ["EvalHelpers.java"], deps = [ ":execution_frame", + ":localized_evaluation_exception", ":planned_interpretable", - ":strict_error_exception", "//common:error_codes", "//common/exceptions:runtime_exception", "//common/values", @@ -370,9 +721,24 @@ java_library( ], ) +cel_android_library( + name = "eval_helpers_android", + srcs = ["EvalHelpers.java"], + deps = [ + ":execution_frame_android", + ":localized_evaluation_exception", + ":planned_interpretable_android", + "//common:error_codes", + "//common/exceptions:runtime_exception", + "//common/values:values_android", + "//runtime:interpretable_android", + ], +) + java_library( - name = "strict_error_exception", - srcs = ["StrictErrorException.java"], + name = "localized_evaluation_exception", + srcs = ["LocalizedEvaluationException.java"], + # used_by_android deps = [ "//common:error_codes", "//common/exceptions:runtime_exception", @@ -389,6 +755,16 @@ java_library( ], ) +cel_android_library( + name = "error_metadata_android", + srcs = ["ErrorMetadata.java"], + deps = [ + "//runtime:metadata", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven_android//:com_google_guava_guava", + ], +) + java_library( name = "planned_interpretable", srcs = ["PlannedInterpretable.java"], @@ -399,3 +775,14 @@ java_library( "@maven//:com_google_errorprone_error_prone_annotations", ], ) + +cel_android_library( + name = "planned_interpretable_android", + srcs = ["PlannedInterpretable.java"], + deps = [ + ":execution_frame_android", + "//runtime:evaluation_exception", + "//runtime:interpretable_android", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalConstant.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalConstant.java index 74d2811ea..2bebb059b 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalConstant.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalConstant.java @@ -14,26 +14,12 @@ package dev.cel.runtime.planner; -import com.google.common.primitives.UnsignedLong; import com.google.errorprone.annotations.Immutable; -import dev.cel.common.values.CelByteString; -import dev.cel.common.values.NullValue; import dev.cel.runtime.GlobalResolver; @Immutable final class EvalConstant extends PlannedInterpretable { - // Pre-allocation of common constants - private static final EvalConstant NULL_VALUE = new EvalConstant(NullValue.NULL_VALUE); - private static final EvalConstant TRUE = new EvalConstant(true); - private static final EvalConstant FALSE = new EvalConstant(false); - private static final EvalConstant ZERO = new EvalConstant(0L); - private static final EvalConstant ONE = new EvalConstant(1L); - private static final EvalConstant UNSIGNED_ZERO = new EvalConstant(UnsignedLong.ZERO); - private static final EvalConstant UNSIGNED_ONE = new EvalConstant(UnsignedLong.ONE); - private static final EvalConstant EMPTY_STRING = new EvalConstant(""); - private static final EvalConstant EMPTY_BYTES = new EvalConstant(CelByteString.EMPTY); - @SuppressWarnings("Immutable") // Known CEL constants that aren't mutated are stored private final Object constant; @@ -42,59 +28,12 @@ public Object eval(GlobalResolver resolver, ExecutionFrame frame) { return constant; } - static EvalConstant create(boolean value) { - return value ? TRUE : FALSE; - } - - static EvalConstant create(String value) { - if (value.isEmpty()) { - return EMPTY_STRING; - } - - return new EvalConstant(value); - } - - static EvalConstant create(long value) { - if (value == 0L) { - return ZERO; - } else if (value == 1L) { - return ONE; - } - - return new EvalConstant(Long.valueOf(value)); - } - - static EvalConstant create(double value) { - return new EvalConstant(Double.valueOf(value)); - } - - static EvalConstant create(UnsignedLong unsignedLong) { - if (unsignedLong.longValue() == 0L) { - return UNSIGNED_ZERO; - } else if (unsignedLong.longValue() == 1L) { - return UNSIGNED_ONE; - } - - return new EvalConstant(unsignedLong); - } - - static EvalConstant create(NullValue unused) { - return NULL_VALUE; - } - - static EvalConstant create(CelByteString byteString) { - if (byteString.isEmpty()) { - return EMPTY_BYTES; - } - return new EvalConstant(byteString); - } - - static EvalConstant create(Object value) { - return new EvalConstant(value); + static EvalConstant create(long exprId, Object value) { + return new EvalConstant(exprId, value); } - private EvalConstant(Object constant) { - super(/* exprId= */ -1); // It's not possible to throw while evaluating a constant + private EvalConstant(long exprId, Object constant) { + super(exprId); this.constant = constant; } } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java index e519b968c..389a21a82 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java @@ -30,7 +30,7 @@ final class EvalCreateList extends PlannedInterpretable { public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEvaluationException { ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize(values.length); for (PlannedInterpretable value : values) { - builder.add(value.eval(resolver, frame)); + builder.add(EvalHelpers.evalStrictly(value, resolver, frame)); } return builder.build(); } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateMap.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateMap.java index abdba90db..4c5a1f0bf 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateMap.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateMap.java @@ -16,9 +16,12 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; import com.google.errorprone.annotations.Immutable; +import dev.cel.common.exceptions.CelDuplicateKeyException; import dev.cel.runtime.CelEvaluationException; import dev.cel.runtime.GlobalResolver; +import java.util.HashSet; @Immutable final class EvalCreateMap extends PlannedInterpretable { @@ -35,13 +38,23 @@ final class EvalCreateMap extends PlannedInterpretable { public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEvaluationException { ImmutableMap.Builder builder = ImmutableMap.builderWithExpectedSize(keys.length); + HashSet keysSeen = Sets.newHashSetWithExpectedSize(keys.length); + for (int i = 0; i < keys.length; i++) { - builder.put(keys[i].eval(resolver, frame), values[i].eval(resolver, frame)); + Object key = keys[i].eval(resolver, frame); + + if (!keysSeen.add(key)) { + throw new LocalizedEvaluationException(CelDuplicateKeyException.of(key), keys[i].exprId()); + } + + builder.put(key, values[i].eval(resolver, frame)); } + return builder.buildOrThrow(); } - static EvalCreateMap create(long exprId, PlannedInterpretable[] keys, PlannedInterpretable[] values) { + static EvalCreateMap create( + long exprId, PlannedInterpretable[] keys, PlannedInterpretable[] values) { return new EvalCreateMap(exprId, keys, values); } 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 de034d035..ee86c9dd6 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java @@ -25,8 +25,8 @@ static Object evalNonstrictly( PlannedInterpretable interpretable, GlobalResolver resolver, ExecutionFrame frame) { try { return interpretable.eval(resolver, frame); - } catch (StrictErrorException e) { - // Intercept the strict exception to get a more localized expr ID for error reporting purposes + } catch (LocalizedEvaluationException e) { + // Intercept the localized exception to get a more specific expr ID for error reporting // Example: foo [1] && strict_err [2] -> ID 2 is propagated. return ErrorValue.create(e.exprId(), e); } catch (Exception e) { @@ -38,10 +38,15 @@ static Object evalStrictly( PlannedInterpretable interpretable, GlobalResolver resolver, ExecutionFrame frame) { try { return interpretable.eval(resolver, frame); + } catch (LocalizedEvaluationException e) { + // Already localized - propagate as-is to preserve inner expression ID + throw e; } catch (CelRuntimeException e) { - throw new StrictErrorException(e, interpretable.exprId()); + // Wrap with current interpretable's location + throw new LocalizedEvaluationException(e, interpretable.exprId()); } catch (Exception e) { - throw new StrictErrorException( + // Wrap generic exceptions with location + throw new LocalizedEvaluationException( e.getCause(), CelErrorCode.INTERNAL_ERROR, interpretable.exprId()); } } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/StrictErrorException.java b/runtime/src/main/java/dev/cel/runtime/planner/LocalizedEvaluationException.java similarity index 55% rename from runtime/src/main/java/dev/cel/runtime/planner/StrictErrorException.java rename to runtime/src/main/java/dev/cel/runtime/planner/LocalizedEvaluationException.java index 441a1f27c..603f0b0a5 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/StrictErrorException.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/LocalizedEvaluationException.java @@ -1,4 +1,4 @@ -// Copyright 2025 Google LLC +// 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. @@ -18,12 +18,16 @@ import dev.cel.common.exceptions.CelRuntimeException; /** - * An exception that's raised when a strict call failed to invoke, which includes the source of - * expression ID, along with canonical CelErrorCode. + * Wraps a {@link CelRuntimeException} with its source expression ID for error reporting. * - *

Note that StrictErrorException should not be surfaced directly back to the user. + *

This is the ONLY exception type that propagates through evaluation in the planner. All + * CelRuntimeExceptions from runtime helpers are immediately wrapped with location information to + * track where the error occurred in the expression tree. + * + *

Note: This exception should not be surfaced directly to users - it's unwrapped in {@link + * PlannedProgram}. */ -final class StrictErrorException extends CelRuntimeException { +final class LocalizedEvaluationException extends CelRuntimeException { private final long exprId; @@ -31,11 +35,11 @@ long exprId() { return exprId; } - StrictErrorException(CelRuntimeException cause, long exprId) { + LocalizedEvaluationException(CelRuntimeException cause, long exprId) { this(cause, cause.getErrorCode(), exprId); } - StrictErrorException(Throwable cause, CelErrorCode errorCode, long exprId) { + LocalizedEvaluationException(Throwable cause, CelErrorCode errorCode, long exprId) { super(cause, errorCode); this.exprId = exprId; } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/MissingAttribute.java b/runtime/src/main/java/dev/cel/runtime/planner/MissingAttribute.java index 596d1bae4..b64c3282c 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/MissingAttribute.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/MissingAttribute.java @@ -15,10 +15,12 @@ package dev.cel.runtime.planner; import com.google.common.collect.ImmutableSet; +import com.google.errorprone.annotations.Immutable; import dev.cel.common.exceptions.CelAttributeNotFoundException; import dev.cel.runtime.GlobalResolver; /** Represents a missing attribute that is surfaced while resolving a struct field or a map key. */ +@Immutable final class MissingAttribute implements Attribute { private final ImmutableSet missingAttributes; diff --git a/runtime/src/main/java/dev/cel/runtime/planner/PlannedInterpretable.java b/runtime/src/main/java/dev/cel/runtime/planner/PlannedInterpretable.java index 5ce3208f8..6f3a9d7ff 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/PlannedInterpretable.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/PlannedInterpretable.java @@ -25,8 +25,6 @@ abstract class PlannedInterpretable { /** Runs interpretation with the given activation which supplies name/value bindings. */ abstract Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEvaluationException; - // TODO: Implement support for late-bound functions and evaluation listener - long exprId() { return exprId; } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java b/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java index aaf26ecb0..22aff43c8 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java @@ -106,9 +106,12 @@ private Object evalOrThrow( private CelEvaluationException newCelEvaluationException(long exprId, Exception e) { CelEvaluationExceptionBuilder builder; - if (e instanceof StrictErrorException) { - // Preserve detailed error, including error codes if one exists. - builder = CelEvaluationExceptionBuilder.newBuilder((CelRuntimeException) e.getCause()); + if (e instanceof LocalizedEvaluationException) { + // Use the localized expr ID (most specific error location) + LocalizedEvaluationException localized = (LocalizedEvaluationException) e; + exprId = localized.exprId(); + builder = + CelEvaluationExceptionBuilder.newBuilder((CelRuntimeException) localized.getCause()); } else if (e instanceof CelRuntimeException) { builder = CelEvaluationExceptionBuilder.newBuilder((CelRuntimeException) e); } else { diff --git a/runtime/src/main/java/dev/cel/runtime/planner/PresenceTestQualifier.java b/runtime/src/main/java/dev/cel/runtime/planner/PresenceTestQualifier.java index 973182b9b..b527719f7 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/PresenceTestQualifier.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/PresenceTestQualifier.java @@ -16,10 +16,12 @@ import static dev.cel.runtime.planner.MissingAttribute.newMissingAttribute; +import com.google.errorprone.annotations.Immutable; import dev.cel.common.values.SelectableValue; import java.util.Map; /** A qualifier for presence testing a field or a map key. */ +@Immutable final class PresenceTestQualifier implements Qualifier { @SuppressWarnings("Immutable") 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 d8c29884e..a24adf214 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java @@ -19,7 +19,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.concurrent.ThreadSafe; +import com.google.errorprone.annotations.Immutable; import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelContainer; import dev.cel.common.CelOptions; @@ -55,10 +55,9 @@ * {@code ProgramPlanner} resolves functions, types, and identifiers at plan time given a * parsed-only or a type-checked expression. */ -@ThreadSafe +@Immutable @Internal public final class ProgramPlanner { - private final CelTypeProvider typeProvider; private final CelValueProvider valueProvider; private final DefaultDispatcher dispatcher; @@ -91,7 +90,7 @@ public Program plan(CelAbstractSyntaxTree ast) throws CelEvaluationException { private PlannedInterpretable plan(CelExpr celExpr, PlannerContext ctx) { switch (celExpr.getKind()) { case CONSTANT: - return planConstant(celExpr.constant()); + return planConstant(celExpr.id(), celExpr.constant()); case IDENT: return planIdent(celExpr, ctx); case SELECT: @@ -134,22 +133,22 @@ private PlannedInterpretable planSelect(CelExpr celExpr, PlannerContext ctx) { return attribute.addQualifier(celExpr.id(), qualifier); } - private PlannedInterpretable planConstant(CelConstant celConstant) { + private PlannedInterpretable planConstant(long exprId, CelConstant celConstant) { switch (celConstant.getKind()) { case NULL_VALUE: - return EvalConstant.create(celConstant.nullValue()); + return EvalConstant.create(exprId, celConstant.nullValue()); case BOOLEAN_VALUE: - return EvalConstant.create(celConstant.booleanValue()); + return EvalConstant.create(exprId, celConstant.booleanValue()); case INT64_VALUE: - return EvalConstant.create(celConstant.int64Value()); + return EvalConstant.create(exprId, celConstant.int64Value()); case UINT64_VALUE: - return EvalConstant.create(celConstant.uint64Value()); + return EvalConstant.create(exprId, celConstant.uint64Value()); case DOUBLE_VALUE: - return EvalConstant.create(celConstant.doubleValue()); + return EvalConstant.create(exprId, celConstant.doubleValue()); case STRING_VALUE: - return EvalConstant.create(celConstant.stringValue()); + return EvalConstant.create(exprId, celConstant.stringValue()); case BYTES_VALUE: - return EvalConstant.create(celConstant.bytesValue()); + return EvalConstant.create(exprId, celConstant.bytesValue()); default: throw new IllegalStateException("Unsupported kind: " + celConstant.getKind()); } @@ -168,7 +167,7 @@ private PlannedInterpretable planIdent(CelExpr celExpr, PlannerContext ctx) { private PlannedInterpretable planCheckedIdent( long id, CelReference identRef, ImmutableMap typeMap) { if (identRef.value().isPresent()) { - return planConstant(identRef.value().get()); + return planConstant(id, identRef.value().get()); } CelType type = typeMap.get(id); @@ -181,7 +180,7 @@ private PlannedInterpretable planCheckedIdent( () -> new NoSuchElementException( "Reference to an undefined type: " + identRef.name())); - return EvalConstant.create(identType); + return EvalConstant.create(id, identType); } return EvalAttribute.create(id, attributeFactory.newAbsoluteAttribute(identRef.name())); diff --git a/runtime/src/main/java/dev/cel/runtime/planner/RelativeAttribute.java b/runtime/src/main/java/dev/cel/runtime/planner/RelativeAttribute.java index a913849f6..1049afc64 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/RelativeAttribute.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/RelativeAttribute.java @@ -16,6 +16,7 @@ import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.Immutable; +import dev.cel.common.values.CelValue; import dev.cel.common.values.CelValueConverter; import dev.cel.runtime.GlobalResolver; @@ -40,6 +41,9 @@ public Object resolve(GlobalResolver ctx, ExecutionFrame frame) { } // TODO: Handle unknowns + if (obj instanceof CelValue) { + obj = celValueConverter.unwrap((CelValue) obj); + } return obj; } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/StringQualifier.java b/runtime/src/main/java/dev/cel/runtime/planner/StringQualifier.java index 4ceaa0e51..25503e65b 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/StringQualifier.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/StringQualifier.java @@ -14,11 +14,13 @@ package dev.cel.runtime.planner; +import com.google.errorprone.annotations.Immutable; import dev.cel.common.exceptions.CelAttributeNotFoundException; import dev.cel.common.values.SelectableValue; import java.util.Map; /** A qualifier that accesses fields or map keys using a string identifier. */ +@Immutable final class StringQualifier implements Qualifier { private final String value; diff --git a/runtime/src/test/java/dev/cel/runtime/BUILD.bazel b/runtime/src/test/java/dev/cel/runtime/BUILD.bazel index ed76fdbe7..ddaf2d222 100644 --- a/runtime/src/test/java/dev/cel/runtime/BUILD.bazel +++ b/runtime/src/test/java/dev/cel/runtime/BUILD.bazel @@ -19,8 +19,8 @@ java_library( # keep sorted exclude = [ "CelLiteInterpreterTest.java", - "CelValueInterpreterTest.java", "InterpreterTest.java", + "PlannerInterpreterTest.java", ] + ANDROID_TESTS, ), deps = [ @@ -51,6 +51,7 @@ java_library( "//common/internal:well_known_proto", "//common/types", "//common/types:cel_v1alpha1_types", + "//common/types:message_lite_type_provider", "//common/types:message_type_provider", "//common/values", "//common/values:cel_byte_string", @@ -125,16 +126,17 @@ java_library( ) java_library( - name = "cel_value_interpreter_test", + name = "planner_interpreter_test", testonly = 1, srcs = [ - "CelValueInterpreterTest.java", + "PlannerInterpreterTest.java", ], deps = [ - # "//java/com/google/testing/testsize:annotations", + "//extensions", + "//runtime", "//testing:base_interpreter_test", - "@maven//:junit_junit", "@maven//:com_google_testparameterinjector_test_parameter_injector", + "@maven//:junit_junit", ], ) @@ -158,6 +160,7 @@ cel_android_local_test( "//runtime:lite_runtime_android", "//runtime:lite_runtime_factory_android", "//runtime:lite_runtime_impl_android", + "//runtime:program_android", "//runtime:standard_functions_android", "//runtime:unknown_attributes_android", "//runtime/src/main/java/dev/cel/runtime:program_android", @@ -203,8 +206,8 @@ junit4_test_suites( src_dir = "src/test/java", deps = [ ":cel_lite_interpreter_test", - ":cel_value_interpreter_test", ":interpreter_test", + ":planner_interpreter_test", ":tests", ], ) diff --git a/runtime/src/test/java/dev/cel/runtime/CelLiteInterpreterTest.java b/runtime/src/test/java/dev/cel/runtime/CelLiteInterpreterTest.java index 088a2d7b0..734719f1a 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelLiteInterpreterTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelLiteInterpreterTest.java @@ -29,13 +29,13 @@ public class CelLiteInterpreterTest extends BaseInterpreterTest { public CelLiteInterpreterTest() { super( - CelRuntimeFactory.standardCelRuntimeBuilder() + CelRuntimeFactory.plannerCelRuntimeBuilder() .setValueProvider( ProtoMessageLiteValueProvider.newInstance( dev.cel.expr.conformance.proto2.TestAllTypesCelDescriptor.getDescriptor(), TestAllTypesCelDescriptor.getDescriptor())) .addLibraries(CelOptionalLibrary.INSTANCE) - .setOptions(newBaseCelOptions().toBuilder().enableCelValue(true).build()) + .setOptions(newBaseCelOptions()) .build()); } diff --git a/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeAndroidTest.java b/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeAndroidTest.java index 638782c2e..fc4c3762e 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeAndroidTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeAndroidTest.java @@ -124,7 +124,7 @@ public void toRuntimeBuilder_isNewInstance() { @Test public void toRuntimeBuilder_propertiesCopied() { - CelOptions celOptions = CelOptions.current().enableCelValue(true).build(); + CelOptions celOptions = CelOptions.current().build(); CelLiteRuntimeLibrary runtimeExtension = CelLiteExtensions.sets(celOptions, SetsFunction.INTERSECTS); CelValueProvider celValueProvider = ProtoMessageLiteValueProvider.newInstance(); @@ -284,6 +284,7 @@ public void eval_customFunctions() throws Exception { public void eval_customFunctions_asLateBoundFunctions() throws Exception { CelLiteRuntime runtime = CelLiteRuntimeFactory.newLiteRuntimeBuilder() + .addLateBoundFunctions("isEmpty") .addFunctionBindings(CelFunctionBinding.from("list_isEmpty", List.class, List::isEmpty)) .setStandardFunctions(CelStandardFunctions.ALL_STANDARD_FUNCTIONS) .build(); @@ -712,7 +713,6 @@ public void eval_protoMessage_mapFields(String checkedExpr) throws Exception { } private enum CelOptionsTestCase { - CEL_VALUE_DISABLED(newBaseTestOptions().enableCelValue(false).build()), UNSIGNED_LONG_DISABLED(newBaseTestOptions().enableUnsignedLongs(false).build()), UNWRAP_WKT_DISABLED(newBaseTestOptions().unwrapWellKnownTypesOnFunctionDispatch(false).build()), STRING_CONCAT_DISABLED(newBaseTestOptions().enableStringConcatenation(false).build()), @@ -723,7 +723,7 @@ private enum CelOptionsTestCase { private final CelOptions celOptions; private static CelOptions.Builder newBaseTestOptions() { - return CelOptions.current().enableCelValue(true); + return CelOptions.current(); } CelOptionsTestCase(CelOptions celOptions) { diff --git a/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeTest.java b/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeTest.java index 4ffe0941c..52570b0a3 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelLiteRuntimeTest.java @@ -42,6 +42,7 @@ import dev.cel.common.CelFunctionDecl; import dev.cel.common.CelOverloadDecl; import dev.cel.common.internal.ProtoTimeUtils; +import dev.cel.common.types.ProtoMessageLiteTypeProvider; import dev.cel.common.types.SimpleType; import dev.cel.common.types.StructTypeReference; import dev.cel.common.values.CelByteString; @@ -74,22 +75,29 @@ /** Exercises tests for CelLiteRuntime using full version of protobuf messages. */ @RunWith(TestParameterInjector.class) public class CelLiteRuntimeTest { + private static final CelContainer CEL_CONTAINER = + CelContainer.ofName("cel.expr.conformance.proto3"); private static final CelCompiler CEL_COMPILER = CelCompilerFactory.standardCelCompilerBuilder() .setStandardMacros(CelStandardMacro.STANDARD_MACROS) .addVar("msg", StructTypeReference.create(TestAllTypes.getDescriptor().getFullName())) .addVar("content", SimpleType.DYN) .addMessageTypes(TestAllTypes.getDescriptor()) - .setContainer(CelContainer.ofName("cel.expr.conformance.proto3")) + .setContainer(CEL_CONTAINER) .build(); private static final CelLiteRuntime CEL_RUNTIME = CelLiteRuntimeFactory.newLiteRuntimeBuilder() .setStandardFunctions(CelStandardFunctions.ALL_STANDARD_FUNCTIONS) + .setTypeProvider( + ProtoMessageLiteTypeProvider.newInstance( + dev.cel.expr.conformance.proto2.TestAllTypesCelDescriptor.getDescriptor(), + TestAllTypesCelDescriptor.getDescriptor())) .setValueProvider( ProtoMessageLiteValueProvider.newInstance( dev.cel.expr.conformance.proto2.TestAllTypesCelDescriptor.getDescriptor(), TestAllTypesCelDescriptor.getDescriptor())) + .setContainer(CEL_CONTAINER) .build(); @Test @@ -623,7 +631,10 @@ public void eval_withLateBoundFunction() throws Exception { CelOverloadDecl.newGlobalOverload( "lateBoundFunc_string", SimpleType.STRING, SimpleType.STRING))) .build(); - CelLiteRuntime celRuntime = CelLiteRuntimeFactory.newLiteRuntimeBuilder().build(); + CelLiteRuntime celRuntime = + CelLiteRuntimeFactory.newLiteRuntimeBuilder() + .addLateBoundFunctions("lateBoundFunc") + .build(); CelAbstractSyntaxTree ast = celCompiler.compile("lateBoundFunc('hello')").getAst(); String result = diff --git a/runtime/src/test/java/dev/cel/runtime/CelRuntimeFactoryTest.java b/runtime/src/test/java/dev/cel/runtime/CelRuntimeFactoryTest.java index 28676ebcb..3abf90f7e 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelRuntimeFactoryTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelRuntimeFactoryTest.java @@ -27,4 +27,10 @@ public final class CelRuntimeFactoryTest { public void standardCelRuntimeBuilder() { assertThat(CelRuntimeFactory.standardCelRuntimeBuilder().build()).isNotNull(); } + + @Test + public void plannerCelRuntimeBuilder() { + CelRuntime runtime = CelRuntimeFactory.plannerCelRuntimeBuilder().build(); + assertThat(runtime).isNotNull(); + } } diff --git a/runtime/src/test/java/dev/cel/runtime/CelRuntimeLegacyImplTest.java b/runtime/src/test/java/dev/cel/runtime/CelRuntimeLegacyImplTest.java index fa3b5f4ae..fec5fab41 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelRuntimeLegacyImplTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelRuntimeLegacyImplTest.java @@ -19,12 +19,10 @@ import com.google.protobuf.Message; import dev.cel.common.CelException; import dev.cel.common.exceptions.CelDivideByZeroException; -import dev.cel.common.values.CelValueProvider; import dev.cel.compiler.CelCompiler; import dev.cel.compiler.CelCompilerFactory; import dev.cel.expr.conformance.proto3.TestAllTypes; import dev.cel.runtime.CelStandardFunctions.StandardFunction; -import java.util.Optional; import java.util.function.Function; import org.junit.Assert; import org.junit.Test; @@ -108,13 +106,11 @@ public void toRuntimeBuilder_optionalProperties() { Function customTypeFactory = (typeName) -> TestAllTypes.newBuilder(); CelStandardFunctions overriddenStandardFunctions = CelStandardFunctions.newBuilder().includeFunctions(StandardFunction.ADD).build(); - CelValueProvider noOpValueProvider = (structType, fields) -> Optional.empty(); CelRuntimeBuilder celRuntimeBuilder = CelRuntimeFactory.standardCelRuntimeBuilder() .setStandardEnvironmentEnabled(false) .setTypeFactory(customTypeFactory) - .setStandardFunctions(overriddenStandardFunctions) - .setValueProvider(noOpValueProvider); + .setStandardFunctions(overriddenStandardFunctions); CelRuntime celRuntime = celRuntimeBuilder.build(); CelRuntimeLegacyImpl.Builder newRuntimeBuilder = @@ -123,6 +119,5 @@ public void toRuntimeBuilder_optionalProperties() { assertThat(newRuntimeBuilder.customTypeFactory).isEqualTo(customTypeFactory); assertThat(newRuntimeBuilder.overriddenStandardFunctions) .isEqualTo(overriddenStandardFunctions); - assertThat(newRuntimeBuilder.celValueProvider).isEqualTo(noOpValueProvider); } } diff --git a/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java b/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java index 7d7243384..477a8fbf6 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java @@ -22,6 +22,7 @@ import com.google.api.expr.v1alpha1.Type.PrimitiveType; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.primitives.UnsignedLong; import com.google.protobuf.Any; import com.google.protobuf.BoolValue; import com.google.protobuf.ByteString; @@ -733,4 +734,15 @@ public void standardEnvironmentDisabledForRuntime_throws() throws Exception { .hasMessageThat() .contains("No matching overload for function 'size'. Overload candidates: size_string"); } + + @Test + public void uintConversion_dynamicDispatch() throws Exception { + CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder().build(); + CelRuntime celRuntime = CelRuntimeFactory.plannerCelRuntimeBuilder().build(); + CelAbstractSyntaxTree ast = celCompiler.compile("uint(dyn(1u))").getAst(); + + Object result = celRuntime.createProgram(ast).eval(); + + assertThat(result).isEqualTo(UnsignedLong.valueOf(1L)); + } } diff --git a/runtime/src/test/java/dev/cel/runtime/CelValueInterpreterTest.java b/runtime/src/test/java/dev/cel/runtime/PlannerInterpreterTest.java similarity index 63% rename from runtime/src/test/java/dev/cel/runtime/CelValueInterpreterTest.java rename to runtime/src/test/java/dev/cel/runtime/PlannerInterpreterTest.java index f56bb3012..7e98df293 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelValueInterpreterTest.java +++ b/runtime/src/test/java/dev/cel/runtime/PlannerInterpreterTest.java @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,16 +15,20 @@ package dev.cel.runtime; import com.google.testing.junit.testparameterinjector.TestParameterInjector; -// import com.google.testing.testsize.MediumTest; +import dev.cel.extensions.CelExtensions; import dev.cel.testing.BaseInterpreterTest; import org.junit.runner.RunWith; -/** Tests for {@link Interpreter} and related functionality using {@code CelValue}. */ -// @MediumTest @RunWith(TestParameterInjector.class) -public class CelValueInterpreterTest extends BaseInterpreterTest { +public class PlannerInterpreterTest extends BaseInterpreterTest { - public CelValueInterpreterTest() { - super(newBaseCelOptions().toBuilder().enableCelValue(true).build()); + public PlannerInterpreterTest() { + super( + CelRuntimeFactory.plannerCelRuntimeBuilder() + .addLateBoundFunctions("record") + .setOptions(newBaseCelOptions()) + .addLibraries(CelExtensions.optional()) + .addFileTypes(TEST_FILE_DESCRIPTORS) + .build()); } } diff --git a/runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel b/runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel index 6749be24f..6f4b2d991 100644 --- a/runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel +++ b/runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel @@ -38,6 +38,7 @@ java_library( "//compiler", "//compiler:compiler_builder", "//extensions", + "//extensions:optional_library", "//parser:macro", "//runtime", "//runtime:dispatcher", 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 44fb16417..060b40447 100644 --- a/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java +++ b/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java @@ -64,6 +64,7 @@ import dev.cel.expr.conformance.proto3.TestAllTypes; import dev.cel.expr.conformance.proto3.TestAllTypes.NestedMessage; import dev.cel.extensions.CelExtensions; +import dev.cel.extensions.CelOptionalLibrary; import dev.cel.parser.CelStandardMacro; import dev.cel.runtime.CelEvaluationException; import dev.cel.runtime.CelFunctionBinding; @@ -79,7 +80,7 @@ @RunWith(TestParameterInjector.class) public final class ProgramPlannerTest { - // Note that the following deps will be built from top-level builder APIs + // Note that the following deps are ordinarily built from top-level builder APIs private static final CelOptions CEL_OPTIONS = CelOptions.current().build(); private static final CelTypeProvider TYPE_PROVIDER = new CombinedCelTypeProvider( @@ -126,6 +127,7 @@ public final class ProgramPlannerTest { .addVar("int_var", SimpleType.INT) .addVar("dyn_var", SimpleType.DYN) .addVar("really.long.abbr.ident", SimpleType.DYN) + .setContainer(CEL_CONTAINER) .addFunctionDeclarations( newFunctionDeclaration("zero", newGlobalOverload("zero_overload", SimpleType.INT)), newFunctionDeclaration("error", newGlobalOverload("error_overload", SimpleType.INT)), @@ -143,9 +145,8 @@ public final class ProgramPlannerTest { "concat_bytes_bytes", SimpleType.BYTES, SimpleType.BYTES, SimpleType.BYTES), newMemberOverload( "bytes_concat_bytes", SimpleType.BYTES, SimpleType.BYTES, SimpleType.BYTES))) + .addLibraries(CelOptionalLibrary.INSTANCE, CelExtensions.comprehensions()) .addMessageTypes(TestAllTypes.getDescriptor()) - .addLibraries(CelExtensions.optional(), CelExtensions.comprehensions()) - .setContainer(CEL_CONTAINER) .build(); /** @@ -326,6 +327,17 @@ public void plan_createMap() throws Exception { assertThat(result).containsExactly("foo", 1L, true, "bar").inOrder(); } + @Test + public void plan_createMap_containsDuplicateKey_throws() throws Exception { + CelAbstractSyntaxTree ast = compile("{true: 1, false: 2, true: 3}"); + Program program = PLANNER.plan(ast); + + CelEvaluationException e = assertThrows(CelEvaluationException.class, program::eval); + assertThat(e) + .hasMessageThat() + .contains("evaluation error at :20: duplicate map key [true]"); + } + @Test public void plan_createStruct() throws Exception { CelAbstractSyntaxTree ast = compile("cel.expr.conformance.proto3.TestAllTypes{}"); diff --git a/runtime/src/test/resources/maps.baseline b/runtime/src/test/resources/maps.baseline index 1e79b815e..ec89b09c6 100644 --- a/runtime/src/test/resources/maps.baseline +++ b/runtime/src/test/resources/maps.baseline @@ -121,7 +121,7 @@ declare map { } =====> bindings: {} -error: evaluation error at test_location:24: duplicate map key [true] +error: evaluation error at test_location:20: duplicate map key [true] error_code: DUPLICATE_ATTRIBUTE Source: {b: 1, !b: 2, b: 3}[true] @@ -139,5 +139,5 @@ declare b { } =====> bindings: {b=true} -error: evaluation error at test_location:15: duplicate map key [true] +error: evaluation error at test_location:14: duplicate map key [true] error_code: DUPLICATE_ATTRIBUTE diff --git a/runtime/src/test/resources/maxComprehension.baseline b/runtime/src/test/resources/maxComprehension.baseline index fc0cc7555..cad955d0f 100644 --- a/runtime/src/test/resources/maxComprehension.baseline +++ b/runtime/src/test/resources/maxComprehension.baseline @@ -4,7 +4,7 @@ declare longlist { } =====> bindings: {longlist=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]} -error: evaluation error: Iteration budget exceeded: 1000 +error: evaluation error at test_location:17: Iteration budget exceeded: 1000 error_code: ITERATION_BUDGET_EXCEEDED Source: longlist.filter(i, i % 2 == 0).map(i, i * 2).map(i, i / 2).size() == 250 @@ -21,7 +21,7 @@ declare longlist { } =====> bindings: {longlist=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499]} -error: evaluation error: Iteration budget exceeded: 1000 +error: evaluation error at test_location:56: Iteration budget exceeded: 1000 error_code: ITERATION_BUDGET_EXCEEDED Source: longlist.map(i, longlist.map(j, longlist.map(k, [i, j, k]))).size() == 9 @@ -38,5 +38,5 @@ declare longlist { } =====> bindings: {longlist=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]} -error: evaluation error: Iteration budget exceeded: 1000 +error: evaluation error at test_location:28: Iteration budget exceeded: 1000 error_code: ITERATION_BUDGET_EXCEEDED