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 7b5c7af4c..ef3cd70f8 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 96e07ac65..57ea2bce5 100644
--- a/common/exceptions/BUILD.bazel
+++ b/common/exceptions/BUILD.bazel
@@ -46,3 +46,15 @@ java_library(
# used_by_android
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
+ exports = ["//common/src/main/java/dev/cel/common/exceptions:overload_not_found"],
+)
diff --git a/common/src/main/java/dev/cel/common/BUILD.bazel b/common/src/main/java/dev/cel/common/BUILD.bazel
index 333ec4b78..54b37bf09 100644
--- a/common/src/main/java/dev/cel/common/BUILD.bazel
+++ b/common/src/main/java/dev/cel/common/BUILD.bazel
@@ -353,6 +353,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 203866928..7006c42a1 100644
--- a/common/src/main/java/dev/cel/common/exceptions/BUILD.bazel
+++ b/common/src/main/java/dev/cel/common/exceptions/BUILD.bazel
@@ -98,3 +98,29 @@ java_library(
"//common/annotations",
],
)
+
+java_library(
+ name = "duplicate_key",
+ srcs = ["CelDuplicateKeyException.java"],
+ # used_by_android
+ tags = [
+ ],
+ deps = [
+ "//common:error_codes",
+ "//common:runtime_exception",
+ "//common/annotations",
+ ],
+)
+
+java_library(
+ name = "overload_not_found",
+ srcs = ["CelOverloadNotFoundException.java"],
+ # used_by_android
+ tags = [
+ ],
+ deps = [
+ "//common:error_codes",
+ "//common:runtime_exception",
+ "//common/annotations",
+ ],
+)
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..ed1cbe5af
--- /dev/null
+++ b/common/src/main/java/dev/cel/common/exceptions/CelDuplicateKeyException.java
@@ -0,0 +1,32 @@
+// 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.CelRuntimeException;
+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/exceptions/CelOverloadNotFoundException.java b/common/src/main/java/dev/cel/common/exceptions/CelOverloadNotFoundException.java
new file mode 100644
index 000000000..9b22ab3d9
--- /dev/null
+++ b/common/src/main/java/dev/cel/common/exceptions/CelOverloadNotFoundException.java
@@ -0,0 +1,44 @@
+// 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.CelRuntimeException;
+import dev.cel.common.annotations.Internal;
+import java.util.Collection;
+import java.util.Collections;
+
+/** Indicates that a matching overload could not be found during function dispatch. */
+@Internal
+public final class CelOverloadNotFoundException extends CelRuntimeException {
+
+ public CelOverloadNotFoundException(String functionName) {
+ this(functionName, Collections.emptyList());
+ }
+
+ public CelOverloadNotFoundException(String functionName, Collection overloadIds) {
+ super(formatErrorMessage(functionName, overloadIds), CelErrorCode.OVERLOAD_NOT_FOUND);
+ }
+
+ private static String formatErrorMessage(String functionName, Collection overloadIds) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("No matching overload for function '").append(functionName).append("'.");
+ if (!overloadIds.isEmpty()) {
+ sb.append(" Overload candidates: ").append(String.join(", ", overloadIds));
+ }
+
+ return sb.toString();
+ }
+}
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 07bfdebbc..709b509f1 100644
--- a/runtime/BUILD.bazel
+++ b/runtime/BUILD.bazel
@@ -16,6 +16,7 @@ java_library(
"//runtime/src/main/java/dev/cel/runtime:descriptor_message_provider",
"//runtime/src/main/java/dev/cel/runtime:function_overload",
"//runtime/src/main/java/dev/cel/runtime:runtime_type_provider",
+ "//runtime/src/main/java/dev/cel/runtime:variable_resolver",
],
)
@@ -27,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 = [
@@ -42,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"],
@@ -78,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"],
)
@@ -111,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"],
@@ -252,12 +276,20 @@ cel_android_library(
java_library(
name = "metadata",
+ # used_by_android
visibility = ["//:internal"],
exports = ["//runtime/src/main/java/dev/cel/runtime:metadata"],
)
+java_library(
+ name = "variable_resolver",
+ # used_by_android
+ exports = ["//runtime/src/main/java/dev/cel/runtime:variable_resolver"],
+)
+
java_library(
name = "concatenated_list_view",
+ # used_by_android
visibility = ["//:internal"],
exports = [
"//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 cf89d0a80..c77c5804e 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",
@@ -123,12 +118,14 @@ java_library(
deps = [
":evaluation_exception",
":evaluation_exception_builder",
+ ":function_binding",
":function_overload",
":function_resolver",
":resolved_overload",
"//:auto_value",
"//common:error_codes",
"//common/annotations",
+ "//common/exceptions:overload_not_found",
"@maven//:com_google_code_findbugs_annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
@@ -138,16 +135,19 @@ java_library(
cel_android_library(
name = "dispatcher_android",
srcs = DISPATCHER_SOURCES,
- visibility = ["//visibility:private"],
+ tags = [
+ ],
deps = [
":evaluation_exception",
":evaluation_exception_builder",
+ ":function_binding_android",
":function_overload_android",
":function_resolver_android",
":resolved_overload_android",
"//:auto_value",
"//common:error_codes",
"//common/annotations",
+ "//common/exceptions:overload_not_found",
"@maven//:com_google_code_findbugs_annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven_android//:com_google_guava_guava",
@@ -172,7 +172,8 @@ java_library(
cel_android_library(
name = "activation_android",
srcs = ["Activation.java"],
- visibility = ["//visibility:private"],
+ tags = [
+ ],
deps = [
":interpretable_android",
":runtime_helpers_android",
@@ -475,10 +476,9 @@ RUNTIME_SOURCES = [
"CelRuntime.java",
"CelRuntimeBuilder.java",
"CelRuntimeFactory.java",
+ "CelRuntimeImpl.java",
"CelRuntimeLegacyImpl.java",
"CelRuntimeLibrary.java",
- "CelVariableResolver.java",
- "HierarchicalVariableResolver.java",
"ProgramImpl.java",
"UnknownContext.java",
]
@@ -570,6 +570,8 @@ java_library(
name = "metadata",
srcs = ["Metadata.java"],
# used_by_android
+ tags = [
+ ],
deps = [
"//common/annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
@@ -579,11 +581,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 +596,8 @@ java_library(
cel_android_library(
name = "interpretable_android",
srcs = INTERPRABLE_SOURCES,
- visibility = ["//visibility:private"],
+ tags = [
+ ],
deps = [
":evaluation_exception",
":evaluation_listener_android",
@@ -611,6 +616,7 @@ java_library(
deps = [
":function_binding",
":runtime_equality",
+ "//common:operator",
"//common:options",
"//common/annotations",
"//runtime/standard:add",
@@ -668,6 +674,7 @@ cel_android_library(
deps = [
":function_binding_android",
":runtime_equality_android",
+ "//common:operator_android",
"//common:options",
"//common/annotations",
"//runtime/standard:add_android",
@@ -725,7 +732,9 @@ java_library(
tags = [
],
deps = [
+ ":evaluation_exception",
":function_overload",
+ "//common/exceptions:overload_not_found",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
@@ -737,7 +746,9 @@ cel_android_library(
tags = [
],
deps = [
+ ":evaluation_exception",
":function_overload_android",
+ "//common/exceptions:overload_not_found",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven_android//:com_google_guava_guava",
],
@@ -776,7 +787,9 @@ java_library(
],
deps = [
":evaluation_exception",
+ "//runtime:unknown_attributes",
"@maven//:com_google_errorprone_error_prone_annotations",
+ "@maven//:com_google_guava_guava",
],
)
@@ -787,7 +800,9 @@ cel_android_library(
],
deps = [
":evaluation_exception",
+ "//runtime:unknown_attributes_android",
"@maven//:com_google_errorprone_error_prone_annotations",
+ "@maven_android//:com_google_guava_guava",
],
)
@@ -798,7 +813,6 @@ java_library(
],
deps = [
":activation",
- ":cel_value_runtime_type_provider",
":descriptor_message_provider",
":descriptor_type_resolver",
":dispatcher",
@@ -815,10 +829,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",
@@ -826,8 +842,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",
@@ -850,8 +874,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",
@@ -866,51 +892,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",
- "@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",
- "//:auto_value",
"@maven//:com_google_errorprone_error_prone_annotations",
+ "@maven//:com_google_guava_guava",
],
)
@@ -920,20 +922,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",
@@ -1026,48 +1031,6 @@ cel_android_library(
],
)
-java_library(
- name = "cel_value_runtime_type_provider",
- srcs = ["CelValueRuntimeTypeProvider.java"],
- deps = [
- ":runtime_type_provider",
- ":unknown_attributes",
- "//common:error_codes",
- "//common:runtime_exception",
- "//common/annotations",
- "//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:error_codes",
- "//common:runtime_exception",
- "//common/annotations",
- "//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"],
@@ -1131,8 +1094,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",
@@ -1177,7 +1142,6 @@ java_library(
],
deps = [
":function_overload",
- ":unknown_attributes",
"//:auto_value",
"//common/annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
@@ -1192,7 +1156,6 @@ cel_android_library(
],
deps = [
":function_overload_android",
- ":unknown_attributes_android",
"//:auto_value",
"//common/annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
@@ -1208,6 +1171,8 @@ java_library(
deps = [
":evaluation_exception",
":function_resolver",
+ ":variable_resolver",
+ "//runtime:variable_resolver",
"@maven//:com_google_errorprone_error_prone_annotations",
],
)
@@ -1220,6 +1185,7 @@ cel_android_library(
deps = [
":evaluation_exception",
":function_resolver_android",
+ ":variable_resolver",
"//:auto_value",
"@maven//:com_google_errorprone_error_prone_annotations",
],
@@ -1250,3 +1216,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/CelFunctionBinding.java b/runtime/src/main/java/dev/cel/runtime/CelFunctionBinding.java
index 79b0f3f54..c7b63926b 100644
--- a/runtime/src/main/java/dev/cel/runtime/CelFunctionBinding.java
+++ b/runtime/src/main/java/dev/cel/runtime/CelFunctionBinding.java
@@ -14,8 +14,13 @@
package dev.cel.runtime;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.Immutable;
+import java.util.Collection;
/**
* Binding consisting of an overload id, a Java-native argument signature, and an overload
@@ -35,7 +40,6 @@
*
* Examples: string_startsWith_string, mathMax_list, lessThan_money_money
*/
-
@Immutable
public interface CelFunctionBinding {
String getOverloadId();
@@ -70,4 +74,23 @@ static CelFunctionBinding from(
return new FunctionBindingImpl(
overloadId, ImmutableList.copyOf(argTypes), impl, /* isStrict= */ true);
}
+
+ /** See {@link #fromOverloads(String, Collection)}. */
+ static ImmutableSet fromOverloads(
+ String functionName, CelFunctionBinding... overloadBindings) {
+ return fromOverloads(functionName, ImmutableList.copyOf(overloadBindings));
+ }
+
+ /**
+ * Creates a set of bindings for a function, enabling dynamic dispatch logic to select the correct
+ * overload at runtime based on argument types.
+ */
+ static ImmutableSet fromOverloads(
+ String functionName, Collection overloadBindings) {
+ checkArgument(!Strings.isNullOrEmpty(functionName), "Function name cannot be null or empty");
+ checkArgument(!overloadBindings.isEmpty(), "You must provide at least one binding.");
+
+ return FunctionBindingImpl.groupOverloadsToFunction(
+ functionName, ImmutableSet.copyOf(overloadBindings));
+ }
}
diff --git a/runtime/src/main/java/dev/cel/runtime/CelFunctionOverload.java b/runtime/src/main/java/dev/cel/runtime/CelFunctionOverload.java
index a1341cb21..3e30a2146 100644
--- a/runtime/src/main/java/dev/cel/runtime/CelFunctionOverload.java
+++ b/runtime/src/main/java/dev/cel/runtime/CelFunctionOverload.java
@@ -14,7 +14,9 @@
package dev.cel.runtime;
+import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.Immutable;
+import java.util.Map;
/** Interface describing the general signature of all CEL custom function implementations. */
@Immutable
@@ -43,4 +45,39 @@ interface Unary {
interface Binary {
Object apply(T1 arg1, T2 arg2) throws CelEvaluationException;
}
+
+ /**
+ * Returns true if the overload's expected argument types match the types of the given arguments.
+ */
+ static boolean canHandle(
+ Object[] arguments, ImmutableList> parameterTypes, boolean isStrict) {
+ if (parameterTypes.size() != arguments.length) {
+ return false;
+ }
+ for (int i = 0; i < parameterTypes.size(); i++) {
+ Class> paramType = parameterTypes.get(i);
+ Object arg = arguments[i];
+ if (arg == null) {
+ // null can be assigned to messages, maps, and to objects.
+ // TODO: Remove null special casing
+ if (paramType != Object.class && !Map.class.isAssignableFrom(paramType)) {
+ return false;
+ }
+ continue;
+ }
+
+ if (arg instanceof Exception || arg instanceof CelUnknownSet) {
+ // Only non-strict functions can accept errors/unknowns as arguments to a function
+ if (!isStrict) {
+ // Skip assignability check below, but continue to validate remaining args
+ continue;
+ }
+ }
+
+ if (!paramType.isAssignableFrom(arg.getClass())) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/runtime/src/main/java/dev/cel/runtime/CelFunctionResolver.java b/runtime/src/main/java/dev/cel/runtime/CelFunctionResolver.java
index 1c5491c14..2fb136a1a 100644
--- a/runtime/src/main/java/dev/cel/runtime/CelFunctionResolver.java
+++ b/runtime/src/main/java/dev/cel/runtime/CelFunctionResolver.java
@@ -15,7 +15,7 @@
package dev.cel.runtime;
import javax.annotation.concurrent.ThreadSafe;
-import java.util.List;
+import java.util.Collection;
import java.util.Optional;
/**
@@ -36,5 +36,18 @@ public interface CelFunctionResolver {
* @throws CelEvaluationException if the overload resolution is ambiguous,
*/
Optional findOverloadMatchingArgs(
- String functionName, List overloadIds, Object[] args) throws CelEvaluationException;
+ String functionName, Collection overloadIds, Object[] args)
+ throws CelEvaluationException;
+
+ /**
+ * Finds a specific function overload to invoke based on given parameters, scanning all available
+ * overloads if necessary.
+ *
+ * @param functionName the logical name of the function being invoked.
+ * @param args The arguments to pass to the function.
+ * @return an optional value of the resolved overload.
+ * @throws CelEvaluationException if the overload resolution is ambiguous.
+ */
+ Optional findOverloadMatchingArgs(String functionName, Object[] args)
+ throws CelEvaluationException;
}
diff --git a/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java b/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java
index f533766b2..c929b96e9 100644
--- a/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java
+++ b/runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java
@@ -19,7 +19,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.Immutable;
import java.util.Arrays;
-import java.util.List;
+import java.util.Collection;
import java.util.Optional;
/**
@@ -35,17 +35,29 @@ private CelLateFunctionBindings(ImmutableMap functi
this.functions = functions;
}
+ public boolean containsFunction(String functionName) {
+ return functions.containsKey(functionName);
+ }
+
@Override
public Optional findOverloadMatchingArgs(
- String functionName, List overloadIds, Object[] args) throws CelEvaluationException {
+ String functionName, Collection overloadIds, Object[] args)
+ throws CelEvaluationException {
return DefaultDispatcher.findOverloadMatchingArgs(functionName, overloadIds, functions, args);
}
+ @Override
+ public Optional findOverloadMatchingArgs(String functionName, Object[] args)
+ throws CelEvaluationException {
+ return DefaultDispatcher.findOverloadMatchingArgs(
+ functionName, functions.keySet(), functions, args);
+ }
+
public static CelLateFunctionBindings from(CelFunctionBinding... functions) {
return from(Arrays.asList(functions));
}
- public static CelLateFunctionBindings from(List functions) {
+ public static CelLateFunctionBindings from(Collection functions) {
return new CelLateFunctionBindings(
functions.stream()
.collect(
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 extends CelLiteRuntimeLibrary> 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/CelResolvedOverload.java b/runtime/src/main/java/dev/cel/runtime/CelResolvedOverload.java
index f6a0c4f99..2bcdf3a2d 100644
--- a/runtime/src/main/java/dev/cel/runtime/CelResolvedOverload.java
+++ b/runtime/src/main/java/dev/cel/runtime/CelResolvedOverload.java
@@ -19,7 +19,6 @@
import com.google.errorprone.annotations.Immutable;
import dev.cel.common.annotations.Internal;
import java.util.List;
-import java.util.Map;
/**
* Representation of a function overload which has been resolved to a specific set of argument types
@@ -80,41 +79,6 @@ public static CelResolvedOverload of(
* Returns true if the overload's expected argument types match the types of the given arguments.
*/
boolean canHandle(Object[] arguments) {
- return canHandle(arguments, getParameterTypes(), isStrict());
- }
-
- /**
- * Returns true if the overload's expected argument types match the types of the given arguments.
- */
- public static boolean canHandle(
- Object[] arguments, ImmutableList> parameterTypes, boolean isStrict) {
- if (parameterTypes.size() != arguments.length) {
- return false;
- }
- for (int i = 0; i < parameterTypes.size(); i++) {
- Class> paramType = parameterTypes.get(i);
- Object arg = arguments[i];
- if (arg == null) {
- // null can be assigned to messages, maps, and to objects.
- // TODO: Remove null special casing
- if (paramType != Object.class && !Map.class.isAssignableFrom(paramType)) {
- return false;
- }
- continue;
- }
-
- if (arg instanceof Exception || arg instanceof CelUnknownSet) {
- // Only non-strict functions can accept errors/unknowns as arguments to a function
- if (!isStrict) {
- // Skip assignability check below, but continue to validate remaining args
- continue;
- }
- }
-
- if (!paramType.isAssignableFrom(arg.getClass())) {
- return false;
- }
- }
- return true;
+ return CelFunctionOverload.canHandle(arguments, getParameterTypes(), isStrict());
}
}
diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntime.java b/runtime/src/main/java/dev/cel/runtime/CelRuntime.java
index a42b7f969..416bca132 100644
--- a/runtime/src/main/java/dev/cel/runtime/CelRuntime.java
+++ b/runtime/src/main/java/dev/cel/runtime/CelRuntime.java
@@ -42,16 +42,6 @@ interface Program extends dev.cel.runtime.Program {
/** Evaluate the expression using {@code message} fields as the source of input variables. */
Object eval(Message message) throws CelEvaluationException;
- /** Evaluate a compiled program with a custom variable {@code resolver}. */
- Object eval(CelVariableResolver resolver) throws CelEvaluationException;
-
- /**
- * Evaluate a compiled program with a custom variable {@code resolver} and late-bound functions
- * {@code lateBoundFunctionResolver}.
- */
- Object eval(CelVariableResolver resolver, CelFunctionResolver lateBoundFunctionResolver)
- throws CelEvaluationException;
-
/**
* Trace evaluates a compiled program without any variables and invokes the listener as
* evaluation progresses through the AST.
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 extends CelRuntimeLibrary> 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/CelStandardFunctions.java b/runtime/src/main/java/dev/cel/runtime/CelStandardFunctions.java
index bedd41728..39797e086 100644
--- a/runtime/src/main/java/dev/cel/runtime/CelStandardFunctions.java
+++ b/runtime/src/main/java/dev/cel/runtime/CelStandardFunctions.java
@@ -15,12 +15,15 @@
package dev.cel.runtime;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.Immutable;
import dev.cel.common.CelOptions;
+import dev.cel.common.Operator;
import dev.cel.common.annotations.Internal;
import dev.cel.runtime.standard.AddOperator;
import dev.cel.runtime.standard.AddOperator.AddOverload;
@@ -104,6 +107,8 @@
import dev.cel.runtime.standard.TimestampFunction.TimestampOverload;
import dev.cel.runtime.standard.UintFunction;
import dev.cel.runtime.standard.UintFunction.UintOverload;
+import java.util.Collection;
+import java.util.Map;
/** Runtime function bindings for the standard functions in CEL. */
@Immutable
@@ -135,7 +140,7 @@ public final class CelStandardFunctions {
GreaterEqualsOverload.GREATER_EQUALS_UINT64_DOUBLE,
GreaterEqualsOverload.GREATER_EQUALS_DOUBLE_UINT64);
- private final ImmutableSet standardOverloads;
+ private final ImmutableMultimap standardOverloads;
public static final ImmutableSet ALL_STANDARD_FUNCTIONS =
ImmutableSet.of(
@@ -187,13 +192,15 @@ public final class CelStandardFunctions {
* special-cased, and does not appear in this enum.
*/
public enum StandardFunction {
- LOGICAL_NOT(LogicalNotOverload.LOGICAL_NOT),
- IN(InOverload.IN_LIST, InOverload.IN_MAP),
- NOT_STRICTLY_FALSE(NotStrictlyFalseOverload.NOT_STRICTLY_FALSE),
- EQUALS(EqualsOverload.EQUALS),
- NOT_EQUALS(NotEqualsOverload.NOT_EQUALS),
- BOOL(BoolOverload.BOOL_TO_BOOL, BoolOverload.STRING_TO_BOOL),
+ LOGICAL_NOT(Operator.LOGICAL_NOT.getFunction(), LogicalNotOverload.LOGICAL_NOT),
+ IN(Operator.IN.getFunction(), InOverload.IN_LIST, InOverload.IN_MAP),
+ NOT_STRICTLY_FALSE(
+ Operator.NOT_STRICTLY_FALSE.getFunction(), NotStrictlyFalseOverload.NOT_STRICTLY_FALSE),
+ EQUALS(Operator.EQUALS.getFunction(), EqualsOverload.EQUALS),
+ NOT_EQUALS(Operator.NOT_EQUALS.getFunction(), NotEqualsOverload.NOT_EQUALS),
+ BOOL("bool", BoolOverload.BOOL_TO_BOOL, BoolOverload.STRING_TO_BOOL),
ADD(
+ Operator.ADD.getFunction(),
AddOverload.ADD_INT64,
AddOverload.ADD_UINT64,
AddOverload.ADD_DOUBLE,
@@ -204,6 +211,7 @@ public enum StandardFunction {
AddOverload.ADD_DURATION_TIMESTAMP,
AddOverload.ADD_DURATION_DURATION),
SUBTRACT(
+ Operator.SUBTRACT.getFunction(),
SubtractOverload.SUBTRACT_INT64,
SubtractOverload.SUBTRACT_TIMESTAMP_TIMESTAMP,
SubtractOverload.SUBTRACT_TIMESTAMP_DURATION,
@@ -211,14 +219,22 @@ public enum StandardFunction {
SubtractOverload.SUBTRACT_DOUBLE,
SubtractOverload.SUBTRACT_DURATION_DURATION),
MULTIPLY(
+ Operator.MULTIPLY.getFunction(),
MultiplyOverload.MULTIPLY_INT64,
MultiplyOverload.MULTIPLY_DOUBLE,
MultiplyOverload.MULTIPLY_UINT64),
- DIVIDE(DivideOverload.DIVIDE_DOUBLE, DivideOverload.DIVIDE_INT64, DivideOverload.DIVIDE_UINT64),
- MODULO(ModuloOverload.MODULO_INT64, ModuloOverload.MODULO_UINT64),
- NEGATE(NegateOverload.NEGATE_INT64, NegateOverload.NEGATE_DOUBLE),
- INDEX(IndexOverload.INDEX_LIST, IndexOverload.INDEX_MAP),
+ DIVIDE(
+ Operator.DIVIDE.getFunction(),
+ DivideOverload.DIVIDE_DOUBLE,
+ DivideOverload.DIVIDE_INT64,
+ DivideOverload.DIVIDE_UINT64),
+ MODULO(
+ Operator.MODULO.getFunction(), ModuloOverload.MODULO_INT64, ModuloOverload.MODULO_UINT64),
+ NEGATE(
+ Operator.NEGATE.getFunction(), NegateOverload.NEGATE_INT64, NegateOverload.NEGATE_DOUBLE),
+ INDEX(Operator.INDEX.getFunction(), IndexOverload.INDEX_LIST, IndexOverload.INDEX_MAP),
SIZE(
+ "size",
SizeOverload.SIZE_STRING,
SizeOverload.SIZE_BYTES,
SizeOverload.SIZE_LIST,
@@ -228,22 +244,26 @@ public enum StandardFunction {
SizeOverload.LIST_SIZE,
SizeOverload.MAP_SIZE),
INT(
+ "int",
IntOverload.INT64_TO_INT64,
IntOverload.UINT64_TO_INT64,
IntOverload.DOUBLE_TO_INT64,
IntOverload.STRING_TO_INT64,
IntOverload.TIMESTAMP_TO_INT64),
UINT(
+ "uint",
UintOverload.UINT64_TO_UINT64,
UintOverload.INT64_TO_UINT64,
UintOverload.DOUBLE_TO_UINT64,
UintOverload.STRING_TO_UINT64),
DOUBLE(
+ "double",
DoubleOverload.DOUBLE_TO_DOUBLE,
DoubleOverload.INT64_TO_DOUBLE,
DoubleOverload.STRING_TO_DOUBLE,
DoubleOverload.UINT64_TO_DOUBLE),
STRING(
+ "string",
StringOverload.STRING_TO_STRING,
StringOverload.INT64_TO_STRING,
StringOverload.DOUBLE_TO_STRING,
@@ -252,51 +272,67 @@ public enum StandardFunction {
StringOverload.TIMESTAMP_TO_STRING,
StringOverload.DURATION_TO_STRING,
StringOverload.UINT64_TO_STRING),
- BYTES(BytesOverload.BYTES_TO_BYTES, BytesOverload.STRING_TO_BYTES),
- DURATION(DurationOverload.DURATION_TO_DURATION, DurationOverload.STRING_TO_DURATION),
+ BYTES("bytes", BytesOverload.BYTES_TO_BYTES, BytesOverload.STRING_TO_BYTES),
+ DURATION(
+ "duration", DurationOverload.DURATION_TO_DURATION, DurationOverload.STRING_TO_DURATION),
TIMESTAMP(
+ "timestamp",
TimestampOverload.STRING_TO_TIMESTAMP,
TimestampOverload.TIMESTAMP_TO_TIMESTAMP,
TimestampOverload.INT64_TO_TIMESTAMP),
- DYN(DynOverload.TO_DYN),
- MATCHES(MatchesOverload.MATCHES, MatchesOverload.MATCHES_STRING),
- CONTAINS(ContainsOverload.CONTAINS_STRING),
- ENDS_WITH(EndsWithOverload.ENDS_WITH_STRING),
- STARTS_WITH(StartsWithOverload.STARTS_WITH_STRING),
+ DYN("dyn", DynOverload.TO_DYN),
+ MATCHES("matches", MatchesOverload.MATCHES, MatchesOverload.MATCHES_STRING),
+ CONTAINS("contains", ContainsOverload.CONTAINS_STRING),
+ ENDS_WITH("endsWith", EndsWithOverload.ENDS_WITH_STRING),
+ STARTS_WITH("startsWith", StartsWithOverload.STARTS_WITH_STRING),
// Date/time Functions
GET_FULL_YEAR(
- GetFullYearOverload.TIMESTAMP_TO_YEAR, GetFullYearOverload.TIMESTAMP_TO_YEAR_WITH_TZ),
- GET_MONTH(GetMonthOverload.TIMESTAMP_TO_MONTH, GetMonthOverload.TIMESTAMP_TO_MONTH_WITH_TZ),
+ "getFullYear",
+ GetFullYearOverload.TIMESTAMP_TO_YEAR,
+ GetFullYearOverload.TIMESTAMP_TO_YEAR_WITH_TZ),
+ GET_MONTH(
+ "getMonth",
+ GetMonthOverload.TIMESTAMP_TO_MONTH,
+ GetMonthOverload.TIMESTAMP_TO_MONTH_WITH_TZ),
GET_DAY_OF_YEAR(
+ "getDayOfYear",
GetDayOfYearOverload.TIMESTAMP_TO_DAY_OF_YEAR,
GetDayOfYearOverload.TIMESTAMP_TO_DAY_OF_YEAR_WITH_TZ),
GET_DAY_OF_MONTH(
+ "getDayOfMonth",
GetDayOfMonthOverload.TIMESTAMP_TO_DAY_OF_MONTH,
GetDayOfMonthOverload.TIMESTAMP_TO_DAY_OF_MONTH_WITH_TZ),
GET_DATE(
+ "getDate",
GetDateOverload.TIMESTAMP_TO_DAY_OF_MONTH_1_BASED,
GetDateOverload.TIMESTAMP_TO_DAY_OF_MONTH_1_BASED_WITH_TZ),
GET_DAY_OF_WEEK(
+ "getDayOfWeek",
GetDayOfWeekOverload.TIMESTAMP_TO_DAY_OF_WEEK,
GetDayOfWeekOverload.TIMESTAMP_TO_DAY_OF_WEEK_WITH_TZ),
GET_HOURS(
+ "getHours",
GetHoursOverload.TIMESTAMP_TO_HOURS,
GetHoursOverload.TIMESTAMP_TO_HOURS_WITH_TZ,
GetHoursOverload.DURATION_TO_HOURS),
GET_MINUTES(
+ "getMinutes",
GetMinutesOverload.TIMESTAMP_TO_MINUTES,
GetMinutesOverload.TIMESTAMP_TO_MINUTES_WITH_TZ,
GetMinutesOverload.DURATION_TO_MINUTES),
GET_SECONDS(
+ "getSeconds",
GetSecondsOverload.TIMESTAMP_TO_SECONDS,
GetSecondsOverload.TIMESTAMP_TO_SECONDS_WITH_TZ,
GetSecondsOverload.DURATION_TO_SECONDS),
GET_MILLISECONDS(
+ "getMilliseconds",
GetMillisecondsOverload.TIMESTAMP_TO_MILLISECONDS,
GetMillisecondsOverload.TIMESTAMP_TO_MILLISECONDS_WITH_TZ,
GetMillisecondsOverload.DURATION_TO_MILLISECONDS),
LESS(
+ Operator.LESS.getFunction(),
LessOverload.LESS_BOOL,
LessOverload.LESS_INT64,
LessOverload.LESS_UINT64,
@@ -312,6 +348,7 @@ public enum StandardFunction {
LessOverload.LESS_UINT64_DOUBLE,
LessOverload.LESS_DOUBLE_UINT64),
LESS_EQUALS(
+ Operator.LESS_EQUALS.getFunction(),
LessEqualsOverload.LESS_EQUALS_BOOL,
LessEqualsOverload.LESS_EQUALS_INT64,
LessEqualsOverload.LESS_EQUALS_UINT64,
@@ -327,6 +364,7 @@ public enum StandardFunction {
LessEqualsOverload.LESS_EQUALS_UINT64_DOUBLE,
LessEqualsOverload.LESS_EQUALS_DOUBLE_UINT64),
GREATER(
+ Operator.GREATER.getFunction(),
GreaterOverload.GREATER_BOOL,
GreaterOverload.GREATER_INT64,
GreaterOverload.GREATER_UINT64,
@@ -342,6 +380,7 @@ public enum StandardFunction {
GreaterOverload.GREATER_UINT64_DOUBLE,
GreaterOverload.GREATER_DOUBLE_UINT64),
GREATER_EQUALS(
+ Operator.GREATER_EQUALS.getFunction(),
GreaterEqualsOverload.GREATER_EQUALS_BOOL,
GreaterEqualsOverload.GREATER_EQUALS_BYTES,
GreaterEqualsOverload.GREATER_EQUALS_DOUBLE,
@@ -357,9 +396,11 @@ public enum StandardFunction {
GreaterEqualsOverload.GREATER_EQUALS_UINT64_DOUBLE,
GreaterEqualsOverload.GREATER_EQUALS_DOUBLE_UINT64);
+ private final String functionName;
private final ImmutableSet standardOverloads;
- StandardFunction(CelStandardOverload... overloads) {
+ StandardFunction(String functionName, CelStandardOverload... overloads) {
+ this.functionName = functionName;
this.standardOverloads = ImmutableSet.copyOf(overloads);
}
@@ -371,15 +412,25 @@ ImmutableSet getOverloads() {
@VisibleForTesting
ImmutableSet getOverloads() {
- return standardOverloads;
+ return ImmutableSet.copyOf(standardOverloads.values());
}
@Internal
public ImmutableSet newFunctionBindings(
RuntimeEquality runtimeEquality, CelOptions celOptions) {
ImmutableSet.Builder builder = ImmutableSet.builder();
- for (CelStandardOverload overload : standardOverloads) {
- builder.add(overload.newFunctionBinding(celOptions, runtimeEquality));
+
+ for (Map.Entry> entry :
+ standardOverloads.asMap().entrySet()) {
+ String functionName = entry.getKey();
+ Collection overloads = entry.getValue();
+
+ ImmutableSet bindings =
+ overloads.stream()
+ .map(o -> o.newFunctionBinding(celOptions, runtimeEquality))
+ .collect(toImmutableSet());
+
+ builder.addAll(CelFunctionBinding.fromOverloads(functionName, bindings));
}
return builder.build();
@@ -454,39 +505,36 @@ public CelStandardFunctions build() {
"You may only populate one of the following builder methods: includeFunctions,"
+ " excludeFunctions or filterFunctions");
- ImmutableSet.Builder standardOverloadBuilder = ImmutableSet.builder();
+ ImmutableMultimap.Builder standardOverloadBuilder =
+ ImmutableMultimap.builder();
for (StandardFunction standardFunction : StandardFunction.values()) {
if (hasIncludeFunctions) {
if (this.includeFunctions.contains(standardFunction)) {
- standardOverloadBuilder.addAll(standardFunction.standardOverloads);
+ standardOverloadBuilder.putAll(
+ standardFunction.functionName, standardFunction.standardOverloads);
}
continue;
}
if (hasExcludeFunctions) {
if (!this.excludeFunctions.contains(standardFunction)) {
- standardOverloadBuilder.addAll(standardFunction.standardOverloads);
+ standardOverloadBuilder.putAll(
+ standardFunction.functionName, standardFunction.standardOverloads);
}
continue;
}
if (hasFilterFunction) {
- ImmutableSet.Builder filteredOverloadsBuilder =
- ImmutableSet.builder();
for (CelStandardOverload standardOverload : standardFunction.standardOverloads) {
boolean includeOverload = functionFilter.include(standardFunction, standardOverload);
if (includeOverload) {
- standardOverloadBuilder.add(standardOverload);
+ standardOverloadBuilder.put(standardFunction.functionName, standardOverload);
}
}
- ImmutableSet filteredOverloads = filteredOverloadsBuilder.build();
- if (!filteredOverloads.isEmpty()) {
- standardOverloadBuilder.addAll(filteredOverloads);
- }
-
continue;
}
- standardOverloadBuilder.addAll(standardFunction.standardOverloads);
+ standardOverloadBuilder.putAll(
+ standardFunction.functionName, standardFunction.standardOverloads);
}
return new CelStandardFunctions(standardOverloadBuilder.build());
@@ -511,7 +559,7 @@ static boolean isHeterogeneousComparison(CelStandardOverload overload) {
return HETEROGENEOUS_COMPARISON_OPERATORS.contains(overload);
}
- private CelStandardFunctions(ImmutableSet standardOverloads) {
+ private CelStandardFunctions(ImmutableMultimap standardOverloads) {
this.standardOverloads = standardOverloads;
}
}
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 989d3b7be..000000000
--- a/runtime/src/main/java/dev/cel/runtime/CelValueRuntimeTypeProvider.java
+++ /dev/null
@@ -1,159 +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.CelErrorCode;
-import dev.cel.common.CelRuntimeException;
-import dev.cel.common.annotations.Internal;
-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 new CelRuntimeException(
- new IllegalArgumentException(String.format("key '%s' is not present in map.", fieldName)),
- CelErrorCode.ATTRIBUTE_NOT_FOUND);
- }
-
- 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 new CelRuntimeException(
- new IllegalArgumentException(
- String.format(
- "Error resolving field '%s'. Field selections must be performed on messages or"
- + " maps.",
- fieldName)),
- CelErrorCode.ATTRIBUTE_NOT_FOUND);
- }
-
- private CelValueRuntimeTypeProvider(
- CelValueProvider valueProvider, BaseProtoCelValueConverter protoCelValueConverter) {
- this.valueProvider = checkNotNull(valueProvider);
- this.protoCelValueConverter = checkNotNull(protoCelValueConverter);
- }
-}
diff --git a/runtime/src/main/java/dev/cel/runtime/DefaultDispatcher.java b/runtime/src/main/java/dev/cel/runtime/DefaultDispatcher.java
index 35ce243b3..0d13f13be 100644
--- a/runtime/src/main/java/dev/cel/runtime/DefaultDispatcher.java
+++ b/runtime/src/main/java/dev/cel/runtime/DefaultDispatcher.java
@@ -19,12 +19,16 @@
import com.google.auto.value.AutoBuilder;
import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.Immutable;
import dev.cel.common.CelErrorCode;
import dev.cel.common.annotations.Internal;
+import dev.cel.common.exceptions.CelOverloadNotFoundException;
+import dev.cel.runtime.FunctionBindingImpl.DynamicDispatchOverload;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -44,16 +48,23 @@ public Optional findOverload(String functionName) {
return Optional.ofNullable(overloads.get(functionName));
}
+ @Override
+ public Optional findOverloadMatchingArgs(String functionName, Object[] args)
+ throws CelEvaluationException {
+ return findOverloadMatchingArgs(functionName, overloads.keySet(), overloads, args);
+ }
+
@Override
public Optional findOverloadMatchingArgs(
- String functionName, List overloadIds, Object[] args) throws CelEvaluationException {
+ String functionName, Collection overloadIds, Object[] args)
+ throws CelEvaluationException {
return findOverloadMatchingArgs(functionName, overloadIds, overloads, args);
}
/** Finds the overload that matches the given function name, overload IDs, and arguments. */
static Optional findOverloadMatchingArgs(
String functionName,
- List overloadIds,
+ Collection overloadIds,
Map overloads,
Object[] args)
throws CelEvaluationException {
@@ -127,7 +138,7 @@ public abstract static class Builder {
@CanIgnoreReturnValue
public Builder addOverload(
String overloadId,
- List> argTypes,
+ ImmutableList> argTypes,
boolean isStrict,
CelFunctionOverload overload) {
checkNotNull(overloadId);
@@ -136,13 +147,36 @@ public Builder addOverload(
checkNotNull(overload);
overloadsBuilder()
- .put(overloadId, CelResolvedOverload.of(overloadId, overload, isStrict, argTypes));
+ .put(
+ overloadId,
+ CelResolvedOverload.of(
+ overloadId,
+ args -> guardedOp(overloadId, args, argTypes, isStrict, overload),
+ isStrict,
+ argTypes));
return this;
}
public abstract DefaultDispatcher build();
}
+ /** Creates an invocation guard around the overload definition. */
+ private static Object guardedOp(
+ String functionName,
+ Object[] args,
+ ImmutableList> argTypes,
+ boolean isStrict,
+ CelFunctionOverload overload)
+ throws CelEvaluationException {
+ // Argument checking for DynamicDispatch is handled inside the overload's apply method itself.
+ if (overload instanceof DynamicDispatchOverload
+ || CelFunctionOverload.canHandle(args, argTypes, isStrict)) {
+ return overload.apply(args);
+ }
+
+ throw new CelOverloadNotFoundException(functionName);
+ }
+
DefaultDispatcher(ImmutableMap overloads) {
this.overloads = overloads;
}
diff --git a/runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java b/runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java
index 47e3d3b13..e7c04bedd 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();
}
diff --git a/runtime/src/main/java/dev/cel/runtime/FunctionBindingImpl.java b/runtime/src/main/java/dev/cel/runtime/FunctionBindingImpl.java
index b554ce41a..48c0eb47a 100644
--- a/runtime/src/main/java/dev/cel/runtime/FunctionBindingImpl.java
+++ b/runtime/src/main/java/dev/cel/runtime/FunctionBindingImpl.java
@@ -14,8 +14,13 @@
package dev.cel.runtime;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.google.errorprone.annotations.Immutable;
+import dev.cel.common.exceptions.CelOverloadNotFoundException;
@Immutable
final class FunctionBindingImpl implements CelFunctionBinding {
@@ -58,4 +63,92 @@ public boolean isStrict() {
this.definition = definition;
this.isStrict = isStrict;
}
+
+ static ImmutableSet groupOverloadsToFunction(
+ String functionName, ImmutableSet overloadBindings) {
+ ImmutableSet.Builder builder = ImmutableSet.builder();
+ builder.addAll(overloadBindings);
+
+ // If there is already a binding with the same name as the function, we treat it as a
+ // "Singleton" binding and do not create a dynamic dispatch wrapper for it.
+ // (Ex: "matches" function)
+ boolean hasSingletonBinding =
+ overloadBindings.stream().anyMatch(b -> b.getOverloadId().equals(functionName));
+
+ if (!hasSingletonBinding) {
+ if (overloadBindings.size() == 1) {
+ CelFunctionBinding singleBinding = Iterables.getOnlyElement(overloadBindings);
+ builder.add(
+ new FunctionBindingImpl(
+ functionName,
+ singleBinding.getArgTypes(),
+ singleBinding.getDefinition(),
+ singleBinding.isStrict()));
+ } else {
+ builder.add(new DynamicDispatchBinding(functionName, overloadBindings));
+ }
+ }
+
+ return builder.build();
+ }
+
+ @Immutable
+ static final class DynamicDispatchBinding implements CelFunctionBinding {
+
+ private final boolean isStrict;
+ private final DynamicDispatchOverload dynamicDispatchOverload;
+
+ @Override
+ public String getOverloadId() {
+ return dynamicDispatchOverload.functionName;
+ }
+
+ @Override
+ public ImmutableList> getArgTypes() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public CelFunctionOverload getDefinition() {
+ return dynamicDispatchOverload;
+ }
+
+ @Override
+ public boolean isStrict() {
+ return isStrict;
+ }
+
+ private DynamicDispatchBinding(
+ String functionName, ImmutableSet overloadBindings) {
+ this.isStrict = overloadBindings.stream().allMatch(CelFunctionBinding::isStrict);
+ this.dynamicDispatchOverload = new DynamicDispatchOverload(functionName, overloadBindings);
+ }
+ }
+
+ @Immutable
+ static final class DynamicDispatchOverload implements CelFunctionOverload {
+ private final String functionName;
+ private final ImmutableSet overloadBindings;
+
+ @Override
+ public Object apply(Object[] args) throws CelEvaluationException {
+ for (CelFunctionBinding overload : overloadBindings) {
+ if (CelFunctionOverload.canHandle(args, overload.getArgTypes(), overload.isStrict())) {
+ return overload.getDefinition().apply(args);
+ }
+ }
+
+ throw new CelOverloadNotFoundException(
+ functionName,
+ overloadBindings.stream()
+ .map(CelFunctionBinding::getOverloadId)
+ .collect(toImmutableList()));
+ }
+
+ private DynamicDispatchOverload(
+ String functionName, ImmutableSet overloadBindings) {
+ this.functionName = functionName;
+ this.overloadBindings = overloadBindings;
+ }
+ }
}
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 e54f848b7..000000000
--- a/runtime/src/main/java/dev/cel/runtime/LiteProgramImpl.java
+++ /dev/null
@@ -1,46 +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);
- }
-
- 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 extends CelLiteRuntimeLibra
return this;
}
+ @Override
+ public CelLiteRuntimeBuilder setContainer(CelContainer container) {
+ this.container = checkNotNull(container);
+ return this;
+ }
+
/** Throws if an unsupported flag in CelOptions is toggled. */
private static void assertAllowedCelOptions(CelOptions celOptions) {
String prefix = "Misconfigured CelOptions: ";
- if (!celOptions.enableCelValue()) {
- throw new IllegalArgumentException(prefix + "enableCelValue must be enabled.");
- }
if (!celOptions.enableUnsignedLongs()) {
throw new IllegalArgumentException(prefix + "enableUnsignedLongs cannot be disabled.");
}
@@ -184,31 +218,57 @@ public CelLiteRuntime build() {
dispatcherBuilder.addOverload(
overloadId, func.getArgTypes(), func.isStrict(), func.getDefinition()));
- Interpreter interpreter =
- new DefaultInterpreter(
- TypeResolver.create(),
- CelValueRuntimeTypeProvider.newInstance(celValueProvider),
+ CelTypeProvider celTypeProvider = DefaultTypeProvider.getInstance();
+ if (this.celTypeProvider != null) {
+ celTypeProvider =
+ new CelTypeProvider.CombinedCelTypeProvider(celTypeProvider, this.celTypeProvider);
+ }
+
+ ProgramPlanner planner =
+ ProgramPlanner.newPlanner(
+ celTypeProvider,
+ celValueProvider,
dispatcherBuilder.build(),
- celOptions);
+ celValueProvider.celValueConverter(),
+ CelContainer.newBuilder().build(),
+ celOptions,
+ lateBoundFunctionNamesBuilder.build());
return new LiteRuntimeImpl(
- interpreter,
celOptions,
customFunctionBindings.values(),
standardFunctions,
runtimeLibs,
- celValueProvider);
+ celValueProvider,
+ celTypeProvider,
+ container,
+ planner);
}
private Builder() {
- this.celOptions =
- CelOptions.current()
- .enableCelValue(true)
- .build();
- this.celValueProvider = (structType, fields) -> 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/Program.java b/runtime/src/main/java/dev/cel/runtime/Program.java
index 3eb074317..c0982f1f8 100644
--- a/runtime/src/main/java/dev/cel/runtime/Program.java
+++ b/runtime/src/main/java/dev/cel/runtime/Program.java
@@ -33,4 +33,14 @@ public interface Program {
*/
Object eval(Map mapValue, CelFunctionResolver lateBoundFunctionResolver)
throws CelEvaluationException;
+
+ /** Evaluate a compiled program with a custom variable {@code resolver}. */
+ Object eval(CelVariableResolver resolver) throws CelEvaluationException;
+
+ /**
+ * Evaluate a compiled program with a custom variable {@code resolver} and late-bound functions
+ * {@code lateBoundFunctionResolver}.
+ */
+ Object eval(CelVariableResolver resolver, CelFunctionResolver lateBoundFunctionResolver)
+ throws CelEvaluationException;
}
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 b99b41fd5..2fa55fad1 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"],
@@ -23,6 +24,7 @@ java_library(
":eval_create_map",
":eval_create_struct",
":eval_fold",
+ ":eval_late_bound_call",
":eval_or",
":eval_test_only",
":eval_unary",
@@ -40,6 +42,7 @@ java_library(
"//common:options",
"//common/annotations",
"//common/ast",
+ "//common/exceptions:overload_not_found",
"//common/types",
"//common/types:type_providers",
"//common/values",
@@ -55,25 +58,100 @@ 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:runtime_exception",
"//common/values",
- "//runtime",
"//runtime:activation",
"//runtime:evaluation_exception",
"//runtime:evaluation_exception_builder",
"//runtime:function_resolver",
"//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: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",
],
)
@@ -84,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"],
@@ -102,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",
@@ -129,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"],
@@ -137,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"],
@@ -144,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",
],
)
@@ -154,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",
],
)
@@ -171,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"],
@@ -185,18 +352,48 @@ 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"],
deps = [
":execution_frame",
":planned_interpretable",
+ "//common/values",
+ "//common/values:cel_value",
"//runtime:evaluation_exception",
"//runtime:interpretable",
"//runtime:resolved_overload",
],
)
+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"],
@@ -204,12 +401,29 @@ java_library(
":eval_helpers",
":execution_frame",
":planned_interpretable",
+ "//common/values",
+ "//common/values:cel_value",
"//runtime:evaluation_exception",
"//runtime:interpretable",
"//runtime:resolved_overload",
],
)
+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"],
@@ -217,12 +431,63 @@ java_library(
":eval_helpers",
":execution_frame",
":planned_interpretable",
+ "//common/values",
+ "//common/values:cel_value",
"//runtime:evaluation_exception",
"//runtime:interpretable",
"//runtime:resolved_overload",
],
)
+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"],
+ deps = [
+ ":eval_helpers",
+ ":execution_frame",
+ ":planned_interpretable",
+ "//common/exceptions:overload_not_found",
+ "//common/values",
+ "//common/values:cel_value",
+ "//runtime:evaluation_exception",
+ "//runtime:interpretable",
+ "//runtime:resolved_overload",
+ "@maven//:com_google_guava_guava",
+ ],
+)
+
+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"],
@@ -236,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"],
@@ -249,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"],
@@ -261,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"],
@@ -277,6 +580,21 @@ 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"],
@@ -290,12 +608,27 @@ java_library(
],
)
+cel_android_library(
+ name = "eval_create_list_android",
+ srcs = ["EvalCreateList.java"],
+ deps = [
+ ":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",
@@ -303,6 +636,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"],
@@ -318,14 +666,42 @@ 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"],
deps = [
"//common:options",
"//common/exceptions:iteration_budget_exceeded",
- "//runtime:interpretable",
- "@maven//:org_jspecify_jspecify",
+ "//runtime:evaluation_exception",
+ "//runtime:function_resolver",
+ "//runtime:resolved_overload",
+ ],
+)
+
+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",
],
)
@@ -334,8 +710,8 @@ java_library(
srcs = ["EvalHelpers.java"],
deps = [
":execution_frame",
+ ":localized_evaluation_exception",
":planned_interpretable",
- ":strict_error_exception",
"//common:error_codes",
"//common:runtime_exception",
"//common/values",
@@ -343,9 +719,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: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:runtime_exception",
@@ -362,6 +753,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"],
@@ -372,3 +773,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/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 8d2805469..fc1a8e196 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/EvalLateBoundCall.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalLateBoundCall.java
new file mode 100644
index 000000000..dedef58ca
--- /dev/null
+++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalLateBoundCall.java
@@ -0,0 +1,80 @@
+// Copyright 2026 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.runtime.planner;
+
+import static dev.cel.runtime.planner.EvalHelpers.evalStrictly;
+
+import com.google.common.collect.ImmutableList;
+import dev.cel.common.exceptions.CelOverloadNotFoundException;
+import dev.cel.common.values.CelValue;
+import dev.cel.common.values.CelValueConverter;
+import dev.cel.runtime.CelEvaluationException;
+import dev.cel.runtime.CelResolvedOverload;
+import dev.cel.runtime.GlobalResolver;
+
+final class EvalLateBoundCall extends PlannedInterpretable {
+
+ private final String functionName;
+ private final ImmutableList overloadIds;
+
+ @SuppressWarnings("Immutable")
+ private final PlannedInterpretable[] args;
+
+ private final CelValueConverter celValueConverter;
+
+ @Override
+ public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEvaluationException {
+ Object[] argVals = new Object[args.length];
+ for (int i = 0; i < args.length; i++) {
+ PlannedInterpretable arg = args[i];
+ // Late bound functions are assumed to be strict.
+ argVals[i] = evalStrictly(arg, resolver, frame);
+ }
+
+ CelResolvedOverload resolvedOverload =
+ frame
+ .findOverload(functionName, overloadIds, argVals)
+ .orElseThrow(() -> new CelOverloadNotFoundException(functionName, overloadIds));
+
+ Object result = resolvedOverload.getDefinition().apply(argVals);
+ Object runtimeValue = celValueConverter.toRuntimeValue(result);
+ if (runtimeValue instanceof CelValue) {
+ return celValueConverter.unwrap((CelValue) runtimeValue);
+ }
+ return runtimeValue;
+ }
+
+ static EvalLateBoundCall create(
+ long exprId,
+ String functionName,
+ ImmutableList overloadIds,
+ PlannedInterpretable[] args,
+ CelValueConverter celValueConverter) {
+ return new EvalLateBoundCall(exprId, functionName, overloadIds, args, celValueConverter);
+ }
+
+ private EvalLateBoundCall(
+ long exprId,
+ String functionName,
+ ImmutableList overloadIds,
+ PlannedInterpretable[] args,
+ CelValueConverter celValueConverter) {
+ super(exprId);
+ this.functionName = functionName;
+ this.overloadIds = overloadIds;
+ this.args = args;
+ this.celValueConverter = celValueConverter;
+ }
+}
diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java
index d1a33017b..e936c511b 100644
--- a/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java
+++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java
@@ -17,6 +17,8 @@
import static dev.cel.runtime.planner.EvalHelpers.evalNonstrictly;
import static dev.cel.runtime.planner.EvalHelpers.evalStrictly;
+import dev.cel.common.values.CelValue;
+import dev.cel.common.values.CelValueConverter;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelResolvedOverload;
import dev.cel.runtime.GlobalResolver;
@@ -25,6 +27,7 @@ final class EvalUnary extends PlannedInterpretable {
private final CelResolvedOverload resolvedOverload;
private final PlannedInterpretable arg;
+ private final CelValueConverter celValueConverter;
@Override
public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEvaluationException {
@@ -34,17 +37,30 @@ public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEval
: evalNonstrictly(arg, resolver, frame);
Object[] arguments = new Object[] {argVal};
- return resolvedOverload.getDefinition().apply(arguments);
+ Object result = resolvedOverload.getDefinition().apply(arguments);
+ Object runtimeValue = celValueConverter.toRuntimeValue(result);
+ if (runtimeValue instanceof CelValue) {
+ return celValueConverter.unwrap((CelValue) runtimeValue);
+ }
+ return runtimeValue;
}
static EvalUnary create(
- long exprId, CelResolvedOverload resolvedOverload, PlannedInterpretable arg) {
- return new EvalUnary(exprId, resolvedOverload, arg);
+ long exprId,
+ CelResolvedOverload resolvedOverload,
+ PlannedInterpretable arg,
+ CelValueConverter celValueConverter) {
+ return new EvalUnary(exprId, resolvedOverload, arg, celValueConverter);
}
- private EvalUnary(long exprId, CelResolvedOverload resolvedOverload, PlannedInterpretable arg) {
+ private EvalUnary(
+ long exprId,
+ CelResolvedOverload resolvedOverload,
+ PlannedInterpretable arg,
+ CelValueConverter celValueConverter) {
super(exprId);
this.resolvedOverload = resolvedOverload;
this.arg = arg;
+ this.celValueConverter = celValueConverter;
}
}
diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java
index da2979ad1..a7f2b643d 100644
--- a/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java
+++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java
@@ -17,16 +17,21 @@
import static dev.cel.runtime.planner.EvalHelpers.evalNonstrictly;
import static dev.cel.runtime.planner.EvalHelpers.evalStrictly;
+import dev.cel.common.values.CelValue;
+import dev.cel.common.values.CelValueConverter;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelResolvedOverload;
import dev.cel.runtime.GlobalResolver;
-@SuppressWarnings("Immutable")
final class EvalVarArgsCall extends PlannedInterpretable {
private final CelResolvedOverload resolvedOverload;
+
+ @SuppressWarnings("Immutable")
private final PlannedInterpretable[] args;
+ private final CelValueConverter celValueConverter;
+
@Override
public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEvaluationException {
Object[] argVals = new Object[args.length];
@@ -38,18 +43,30 @@ public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEval
: evalNonstrictly(arg, resolver, frame);
}
- return resolvedOverload.getDefinition().apply(argVals);
+ Object result = resolvedOverload.getDefinition().apply(argVals);
+ Object runtimeValue = celValueConverter.toRuntimeValue(result);
+ if (runtimeValue instanceof CelValue) {
+ return celValueConverter.unwrap((CelValue) runtimeValue);
+ }
+ return runtimeValue;
}
static EvalVarArgsCall create(
- long exprId, CelResolvedOverload resolvedOverload, PlannedInterpretable[] args) {
- return new EvalVarArgsCall(exprId, resolvedOverload, args);
+ long exprId,
+ CelResolvedOverload resolvedOverload,
+ PlannedInterpretable[] args,
+ CelValueConverter celValueConverter) {
+ return new EvalVarArgsCall(exprId, resolvedOverload, args, celValueConverter);
}
private EvalVarArgsCall(
- long exprId, CelResolvedOverload resolvedOverload, PlannedInterpretable[] args) {
+ long exprId,
+ CelResolvedOverload resolvedOverload,
+ PlannedInterpretable[] args,
+ CelValueConverter celValueConverter) {
super(exprId);
this.resolvedOverload = resolvedOverload;
this.args = args;
+ this.celValueConverter = celValueConverter;
}
}
diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java
index 6bda7619d..1a4218860 100644
--- a/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java
+++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java
@@ -14,6 +14,8 @@
package dev.cel.runtime.planner;
+import dev.cel.common.values.CelValue;
+import dev.cel.common.values.CelValueConverter;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelResolvedOverload;
import dev.cel.runtime.GlobalResolver;
@@ -22,18 +24,27 @@ final class EvalZeroArity extends PlannedInterpretable {
private static final Object[] EMPTY_ARRAY = new Object[0];
private final CelResolvedOverload resolvedOverload;
+ private final CelValueConverter celValueConverter;
@Override
public Object eval(GlobalResolver resolver, ExecutionFrame frame) throws CelEvaluationException {
- return resolvedOverload.getDefinition().apply(EMPTY_ARRAY);
+ Object result = resolvedOverload.getDefinition().apply(EMPTY_ARRAY);
+ Object runtimeValue = celValueConverter.toRuntimeValue(result);
+ if (runtimeValue instanceof CelValue) {
+ return celValueConverter.unwrap((CelValue) runtimeValue);
+ }
+ return runtimeValue;
}
- static EvalZeroArity create(long exprId, CelResolvedOverload resolvedOverload) {
- return new EvalZeroArity(exprId, resolvedOverload);
+ static EvalZeroArity create(
+ long exprId, CelResolvedOverload resolvedOverload, CelValueConverter celValueConverter) {
+ return new EvalZeroArity(exprId, resolvedOverload, celValueConverter);
}
- private EvalZeroArity(long exprId, CelResolvedOverload resolvedOverload) {
+ private EvalZeroArity(
+ long exprId, CelResolvedOverload resolvedOverload, CelValueConverter celValueConverter) {
super(exprId);
this.resolvedOverload = resolvedOverload;
+ this.celValueConverter = celValueConverter;
}
}
diff --git a/runtime/src/main/java/dev/cel/runtime/planner/ExecutionFrame.java b/runtime/src/main/java/dev/cel/runtime/planner/ExecutionFrame.java
index a436d397a..80ee4b318 100644
--- a/runtime/src/main/java/dev/cel/runtime/planner/ExecutionFrame.java
+++ b/runtime/src/main/java/dev/cel/runtime/planner/ExecutionFrame.java
@@ -16,19 +16,26 @@
import dev.cel.common.CelOptions;
import dev.cel.common.exceptions.CelIterationLimitExceededException;
-import dev.cel.runtime.GlobalResolver;
-import org.jspecify.annotations.Nullable;
+import dev.cel.runtime.CelEvaluationException;
+import dev.cel.runtime.CelFunctionResolver;
+import dev.cel.runtime.CelResolvedOverload;
+import java.util.Collection;
+import java.util.Optional;
/** Tracks execution context within a planned program. */
-final class ExecutionFrame implements GlobalResolver {
+final class ExecutionFrame {
- private final GlobalResolver delegate;
private final int comprehensionIterationLimit;
+ private final CelFunctionResolver functionResolver;
private int iterationCount;
- @Override
- public @Nullable Object resolve(String name) {
- return delegate.resolve(name);
+ Optional findOverload(
+ String functionName, Collection overloadIds, Object[] args)
+ throws CelEvaluationException {
+ if (overloadIds.isEmpty()) {
+ return functionResolver.findOverloadMatchingArgs(functionName, args);
+ }
+ return functionResolver.findOverloadMatchingArgs(functionName, overloadIds, args);
}
void incrementIterations() {
@@ -40,12 +47,12 @@ void incrementIterations() {
}
}
- static ExecutionFrame create(GlobalResolver delegate, CelOptions celOptions) {
- return new ExecutionFrame(delegate, celOptions.comprehensionMaxIterations());
+ static ExecutionFrame create(CelFunctionResolver functionResolver, CelOptions celOptions) {
+ return new ExecutionFrame(functionResolver, celOptions.comprehensionMaxIterations());
}
- private ExecutionFrame(GlobalResolver delegate, int limit) {
- this.delegate = delegate;
+ private ExecutionFrame(CelFunctionResolver functionResolver, int limit) {
this.comprehensionIterationLimit = limit;
+ this.functionResolver = functionResolver;
}
}
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 54%
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 3acd6ff27..b41334a23 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.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/PlannedProgram.java b/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java
index 646ad6c85..df52b9691 100644
--- a/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java
+++ b/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java
@@ -23,13 +23,33 @@
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelEvaluationExceptionBuilder;
import dev.cel.runtime.CelFunctionResolver;
+import dev.cel.runtime.CelResolvedOverload;
+import dev.cel.runtime.CelVariableResolver;
import dev.cel.runtime.GlobalResolver;
import dev.cel.runtime.Program;
+import java.util.Collection;
import java.util.Map;
+import java.util.Optional;
@Immutable
@AutoValue
abstract class PlannedProgram implements Program {
+
+ private static final CelFunctionResolver EMPTY_FUNCTION_RESOLVER =
+ new CelFunctionResolver() {
+ @Override
+ public Optional findOverloadMatchingArgs(
+ String functionName, Collection overloadIds, Object[] args) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional findOverloadMatchingArgs(
+ String functionName, Object[] args) {
+ return Optional.empty();
+ }
+ };
+
abstract PlannedInterpretable interpretable();
abstract ErrorMetadata metadata();
@@ -38,24 +58,40 @@ abstract class PlannedProgram implements Program {
@Override
public Object eval() throws CelEvaluationException {
- return evalOrThrow(interpretable(), GlobalResolver.EMPTY);
+ return evalOrThrow(interpretable(), GlobalResolver.EMPTY, EMPTY_FUNCTION_RESOLVER);
}
@Override
public Object eval(Map mapValue) throws CelEvaluationException {
- return evalOrThrow(interpretable(), Activation.copyOf(mapValue));
+ return evalOrThrow(interpretable(), Activation.copyOf(mapValue), EMPTY_FUNCTION_RESOLVER);
}
@Override
public Object eval(Map mapValue, CelFunctionResolver lateBoundFunctionResolver)
throws CelEvaluationException {
- throw new UnsupportedOperationException("Late bound functions not supported yet");
+ return evalOrThrow(interpretable(), Activation.copyOf(mapValue), lateBoundFunctionResolver);
+ }
+
+ @Override
+ public Object eval(CelVariableResolver resolver) throws CelEvaluationException {
+ return evalOrThrow(
+ interpretable(), (name) -> resolver.find(name).orElse(null), EMPTY_FUNCTION_RESOLVER);
+ }
+
+ @Override
+ public Object eval(CelVariableResolver resolver, CelFunctionResolver lateBoundFunctionResolver)
+ throws CelEvaluationException {
+ return evalOrThrow(
+ interpretable(), (name) -> resolver.find(name).orElse(null), lateBoundFunctionResolver);
}
- private Object evalOrThrow(PlannedInterpretable interpretable, GlobalResolver resolver)
+ private Object evalOrThrow(
+ PlannedInterpretable interpretable,
+ GlobalResolver resolver,
+ CelFunctionResolver functionResolver)
throws CelEvaluationException {
try {
- ExecutionFrame frame = ExecutionFrame.create(resolver, options());
+ ExecutionFrame frame = ExecutionFrame.create(functionResolver, options());
Object evalResult = interpretable.eval(resolver, frame);
if (evalResult instanceof ErrorValue) {
ErrorValue errorValue = (ErrorValue) evalResult;
@@ -70,9 +106,12 @@ private Object evalOrThrow(PlannedInterpretable interpretable, GlobalResolver re
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 1559b8482..a24adf214 100644
--- a/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java
+++ b/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java
@@ -17,8 +17,9 @@
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
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;
@@ -34,6 +35,7 @@
import dev.cel.common.ast.CelExpr.CelStruct;
import dev.cel.common.ast.CelExpr.CelStruct.Entry;
import dev.cel.common.ast.CelReference;
+import dev.cel.common.exceptions.CelOverloadNotFoundException;
import dev.cel.common.types.CelKind;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypeProvider;
@@ -53,17 +55,17 @@
* {@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;
private final AttributeFactory attributeFactory;
private final CelContainer container;
private final CelOptions options;
-
+ private final CelValueConverter celValueConverter;
+ private final ImmutableSet lateBoundFunctionNames;
/**
* Plans a {@link Program} from the provided parsed-only or type-checked {@link
@@ -71,21 +73,24 @@ public final class ProgramPlanner {
*/
public Program plan(CelAbstractSyntaxTree ast) throws CelEvaluationException {
PlannedInterpretable plannedInterpretable;
+ ErrorMetadata errorMetadata =
+ ErrorMetadata.create(ast.getSource().getPositionsMap(), ast.getSource().getDescription());
try {
plannedInterpretable = plan(ast.getExpr(), PlannerContext.create(ast));
} catch (RuntimeException e) {
- throw CelEvaluationExceptionBuilder.newBuilder(e.getMessage()).setCause(e).build();
+ throw CelEvaluationExceptionBuilder.newBuilder(e.getMessage())
+ .setMetadata(errorMetadata, ast.getExpr().id())
+ .setCause(e)
+ .build();
}
- ErrorMetadata errorMetadata =
- ErrorMetadata.create(ast.getSource().getPositionsMap(), ast.getSource().getDescription());
return PlannedProgram.create(plannedInterpretable, errorMetadata, options);
}
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:
@@ -128,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());
}
@@ -162,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);
@@ -175,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()));
@@ -224,19 +229,36 @@ private PlannedInterpretable planCall(CelExpr expr, PlannerContext ctx) {
if (resolvedOverload == null) {
// Parsed-only function dispatch
- resolvedOverload =
- dispatcher
- .findOverload(functionName)
- .orElseThrow(() -> new NoSuchElementException("Overload not found: " + functionName));
+ resolvedOverload = dispatcher.findOverload(functionName).orElse(null);
+ }
+
+ if (resolvedOverload == null) {
+ if (!lateBoundFunctionNames.contains(functionName)) {
+ CelReference reference = ctx.referenceMap().get(expr.id());
+ if (reference != null) {
+ throw new CelOverloadNotFoundException(functionName, reference.overloadIds());
+ } else {
+ throw new CelOverloadNotFoundException(functionName);
+ }
+ }
+
+ ImmutableList overloadIds = ImmutableList.of();
+ if (resolvedFunction.overloadId().isPresent()) {
+ overloadIds = ImmutableList.of(resolvedFunction.overloadId().get());
+ }
+
+ return EvalLateBoundCall.create(
+ expr.id(), functionName, overloadIds, evaluatedArgs, celValueConverter);
}
switch (argCount) {
case 0:
- return EvalZeroArity.create(expr.id(), resolvedOverload);
+ return EvalZeroArity.create(expr.id(), resolvedOverload, celValueConverter);
case 1:
- return EvalUnary.create(expr.id(), resolvedOverload, evaluatedArgs[0]);
+ return EvalUnary.create(expr.id(), resolvedOverload, evaluatedArgs[0], celValueConverter);
default:
- return EvalVarArgsCall.create(expr.id(), resolvedOverload, evaluatedArgs);
+ return EvalVarArgsCall.create(
+ expr.id(), resolvedOverload, evaluatedArgs, celValueConverter);
}
}
@@ -455,9 +477,16 @@ public static ProgramPlanner newPlanner(
DefaultDispatcher dispatcher,
CelValueConverter celValueConverter,
CelContainer container,
- CelOptions options) {
+ CelOptions options,
+ ImmutableSet lateBoundFunctionNames) {
return new ProgramPlanner(
- typeProvider, valueProvider, dispatcher, celValueConverter, container, options);
+ typeProvider,
+ valueProvider,
+ dispatcher,
+ celValueConverter,
+ container,
+ options,
+ lateBoundFunctionNames);
}
private ProgramPlanner(
@@ -466,12 +495,15 @@ private ProgramPlanner(
DefaultDispatcher dispatcher,
CelValueConverter celValueConverter,
CelContainer container,
- CelOptions options) {
+ CelOptions options,
+ ImmutableSet lateBoundFunctionNames) {
this.typeProvider = typeProvider;
this.valueProvider = valueProvider;
this.dispatcher = dispatcher;
+ this.celValueConverter = celValueConverter;
this.container = container;
this.options = options;
+ this.lateBoundFunctionNames = lateBoundFunctionNames;
this.attributeFactory =
AttributeFactory.newAttributeFactory(container, typeProvider, celValueConverter);
}
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/main/java/dev/cel/runtime/standard/CelStandardFunction.java b/runtime/src/main/java/dev/cel/runtime/standard/CelStandardFunction.java
index 73d53287c..f9f919413 100644
--- a/runtime/src/main/java/dev/cel/runtime/standard/CelStandardFunction.java
+++ b/runtime/src/main/java/dev/cel/runtime/standard/CelStandardFunction.java
@@ -15,6 +15,7 @@
package dev.cel.runtime.standard;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
@@ -34,12 +35,12 @@ public abstract class CelStandardFunction {
public ImmutableSet newFunctionBindings(
CelOptions celOptions, RuntimeEquality runtimeEquality) {
- ImmutableSet.Builder builder = ImmutableSet.builder();
- for (CelStandardOverload overload : overloads) {
- builder.add(overload.newFunctionBinding(celOptions, runtimeEquality));
- }
+ ImmutableSet overloadBindings =
+ overloads.stream()
+ .map(overload -> overload.newFunctionBinding(celOptions, runtimeEquality))
+ .collect(toImmutableSet());
- return builder.build();
+ return CelFunctionBinding.fromOverloads(name, overloadBindings);
}
CelStandardFunction(String name, ImmutableSet overloads) {
diff --git a/runtime/src/test/java/dev/cel/runtime/BUILD.bazel b/runtime/src/test/java/dev/cel/runtime/BUILD.bazel
index eb0d652f1..bb4ca4cb8 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 = [
@@ -48,6 +48,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",
@@ -122,16 +123,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",
],
)
@@ -155,6 +157,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",
@@ -200,8 +203,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 c4a041f6a..d488095d4 100644
--- a/runtime/src/test/java/dev/cel/runtime/CelRuntimeLegacyImplTest.java
+++ b/runtime/src/test/java/dev/cel/runtime/CelRuntimeLegacyImplTest.java
@@ -18,12 +18,10 @@
import com.google.protobuf.Message;
import dev.cel.common.CelException;
-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;
@@ -107,13 +105,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 =
@@ -122,6 +118,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 01df7c9ee..9b588b77e 100644
--- a/runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel
+++ b/runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel
@@ -20,7 +20,6 @@ java_library(
"//common:compiler_common",
"//common:container",
"//common:error_codes",
- "//common:operator",
"//common:options",
"//common/ast",
"//common/internal:cel_descriptor_pools",
@@ -38,26 +37,16 @@ java_library(
"//compiler",
"//compiler:compiler_builder",
"//extensions",
+ "//extensions:optional_library",
"//parser:macro",
"//runtime",
"//runtime:dispatcher",
"//runtime:function_binding",
"//runtime:program",
- "//runtime:resolved_overload",
"//runtime:runtime_equality",
"//runtime:runtime_helpers",
+ "//runtime:standard_functions",
"//runtime/planner:program_planner",
- "//runtime/standard:add",
- "//runtime/standard:divide",
- "//runtime/standard:dyn",
- "//runtime/standard:equals",
- "//runtime/standard:greater",
- "//runtime/standard:greater_equals",
- "//runtime/standard:index",
- "//runtime/standard:less",
- "//runtime/standard:logical_not",
- "//runtime/standard:not_strictly_false",
- "//runtime/standard:standard_function",
"@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto",
"@maven//:com_google_guava_guava",
"@maven//:com_google_testparameterinjector_test_parameter_injector",
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 968fdcc94..c41162514 100644
--- a/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java
+++ b/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java
@@ -25,7 +25,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
import com.google.common.primitives.UnsignedLong;
import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
@@ -36,7 +35,6 @@
import dev.cel.common.CelErrorCode;
import dev.cel.common.CelOptions;
import dev.cel.common.CelSource;
-import dev.cel.common.Operator;
import dev.cel.common.ast.CelExpr;
import dev.cel.common.internal.CelDescriptorPool;
import dev.cel.common.internal.DefaultDescriptorPool;
@@ -65,32 +63,23 @@
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;
-import dev.cel.runtime.CelFunctionOverload;
-import dev.cel.runtime.CelResolvedOverload;
+import dev.cel.runtime.CelLateFunctionBindings;
+import dev.cel.runtime.CelStandardFunctions;
+import dev.cel.runtime.CelStandardFunctions.StandardFunction;
import dev.cel.runtime.DefaultDispatcher;
import dev.cel.runtime.Program;
import dev.cel.runtime.RuntimeEquality;
import dev.cel.runtime.RuntimeHelpers;
-import dev.cel.runtime.standard.AddOperator;
-import dev.cel.runtime.standard.CelStandardFunction;
-import dev.cel.runtime.standard.DivideOperator;
-import dev.cel.runtime.standard.DynFunction;
-import dev.cel.runtime.standard.EqualsOperator;
-import dev.cel.runtime.standard.GreaterEqualsOperator;
-import dev.cel.runtime.standard.GreaterOperator;
-import dev.cel.runtime.standard.IndexOperator;
-import dev.cel.runtime.standard.LessOperator;
-import dev.cel.runtime.standard.LogicalNotOperator;
-import dev.cel.runtime.standard.NotStrictlyFalseFunction;
import org.junit.Test;
import org.junit.runner.RunWith;
@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(
@@ -121,16 +110,23 @@ public final class ProgramPlannerTest {
newDispatcher(),
CEL_VALUE_CONVERTER,
CEL_CONTAINER,
- CEL_OPTIONS);
+ CEL_OPTIONS,
+ ImmutableSet.of("late_bound_func"));
private static final CelCompiler CEL_COMPILER =
CelCompilerFactory.standardCelCompilerBuilder()
.setStandardMacros(CelStandardMacro.STANDARD_MACROS)
+ .addFunctionDeclarations(
+ newFunctionDeclaration(
+ "late_bound_func",
+ newGlobalOverload(
+ "late_bound_func_overload", SimpleType.STRING, SimpleType.STRING)))
.addVar("msg", StructTypeReference.create(TestAllTypes.getDescriptor().getFullName()))
.addVar("map_var", MapType.create(SimpleType.STRING, SimpleType.DYN))
.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)),
@@ -148,9 +144,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();
/**
@@ -161,136 +156,88 @@ private static DefaultDispatcher newDispatcher() {
DefaultDispatcher.Builder builder = DefaultDispatcher.newBuilder();
// Subsetted StdLib
- addBindings(
- builder, Operator.INDEX.getFunction(), fromStandardFunction(IndexOperator.create()));
- addBindings(
- builder,
- Operator.LOGICAL_NOT.getFunction(),
- fromStandardFunction(LogicalNotOperator.create()));
- addBindings(builder, Operator.ADD.getFunction(), fromStandardFunction(AddOperator.create()));
- addBindings(
- builder, Operator.GREATER.getFunction(), fromStandardFunction(GreaterOperator.create()));
- addBindings(
- builder,
- Operator.GREATER_EQUALS.getFunction(),
- fromStandardFunction(GreaterEqualsOperator.create()));
- addBindings(builder, Operator.LESS.getFunction(), fromStandardFunction(LessOperator.create()));
- addBindings(
- builder, Operator.DIVIDE.getFunction(), fromStandardFunction(DivideOperator.create()));
- addBindings(
- builder, Operator.EQUALS.getFunction(), fromStandardFunction(EqualsOperator.create()));
- addBindings(
- builder,
- Operator.NOT_STRICTLY_FALSE.getFunction(),
- fromStandardFunction(NotStrictlyFalseFunction.create()));
- addBindings(builder, "dyn", fromStandardFunction(DynFunction.create()));
+ CelStandardFunctions stdFunctions =
+ CelStandardFunctions.newBuilder()
+ .includeFunctions(
+ StandardFunction.INDEX,
+ StandardFunction.LOGICAL_NOT,
+ StandardFunction.ADD,
+ StandardFunction.GREATER,
+ StandardFunction.GREATER_EQUALS,
+ StandardFunction.LESS,
+ StandardFunction.DIVIDE,
+ StandardFunction.EQUALS,
+ StandardFunction.NOT_STRICTLY_FALSE,
+ StandardFunction.DYN)
+ .build();
+ addBindingsToDispatcher(
+ builder, stdFunctions.newFunctionBindings(RUNTIME_EQUALITY, CEL_OPTIONS));
// Custom functions
- addBindings(
+ addBindingsToDispatcher(
builder,
- "zero",
- CelFunctionBinding.from("zero_overload", ImmutableList.of(), (unused) -> 0L));
- addBindings(
+ CelFunctionBinding.fromOverloads(
+ "zero", CelFunctionBinding.from("zero_overload", ImmutableList.of(), (unused) -> 0L)));
+
+ addBindingsToDispatcher(
builder,
- "error",
- CelFunctionBinding.from(
- "error_overload",
- ImmutableList.of(),
- (unused) -> {
- throw new IllegalArgumentException("Intentional error");
- }));
- addBindings(
+ CelFunctionBinding.fromOverloads(
+ "error",
+ CelFunctionBinding.from(
+ "error_overload",
+ ImmutableList.of(),
+ (unused) -> {
+ throw new IllegalArgumentException("Intentional error");
+ })));
+
+ addBindingsToDispatcher(
builder,
- "neg",
- CelFunctionBinding.from("neg_int", Long.class, arg -> -arg),
- CelFunctionBinding.from("neg_double", Double.class, arg -> -arg));
- addBindings(
+ CelFunctionBinding.fromOverloads(
+ "neg",
+ CelFunctionBinding.from("neg_int", Long.class, arg -> -arg),
+ CelFunctionBinding.from("neg_double", Double.class, arg -> -arg)));
+
+ addBindingsToDispatcher(
builder,
- "cel.expr.conformance.proto3.power",
- CelFunctionBinding.from(
- "power_int_int",
- Long.class,
- Long.class,
- (value, power) -> (long) Math.pow(value, power)));
- addBindings(
+ CelFunctionBinding.fromOverloads(
+ "cel.expr.conformance.proto3.power",
+ CelFunctionBinding.from(
+ "power_int_int",
+ Long.class,
+ Long.class,
+ (value, power) -> (long) Math.pow(value, power))));
+
+ addBindingsToDispatcher(
builder,
- "concat",
- CelFunctionBinding.from(
- "concat_bytes_bytes",
- CelByteString.class,
- CelByteString.class,
- ProgramPlannerTest::concatenateByteArrays),
- CelFunctionBinding.from(
- "bytes_concat_bytes",
- CelByteString.class,
- CelByteString.class,
- ProgramPlannerTest::concatenateByteArrays));
+ CelFunctionBinding.fromOverloads(
+ "concat",
+ CelFunctionBinding.from(
+ "concat_bytes_bytes",
+ CelByteString.class,
+ CelByteString.class,
+ ProgramPlannerTest::concatenateByteArrays),
+ CelFunctionBinding.from(
+ "bytes_concat_bytes",
+ CelByteString.class,
+ CelByteString.class,
+ ProgramPlannerTest::concatenateByteArrays)));
return builder.build();
}
- private static void addBindings(
- DefaultDispatcher.Builder builder,
- String functionName,
- CelFunctionBinding... functionBindings) {
- addBindings(builder, functionName, ImmutableSet.copyOf(functionBindings));
- }
-
- private static void addBindings(
- DefaultDispatcher.Builder builder,
- String functionName,
- ImmutableCollection overloadBindings) {
+ private static void addBindingsToDispatcher(
+ DefaultDispatcher.Builder builder, ImmutableCollection overloadBindings) {
if (overloadBindings.isEmpty()) {
throw new IllegalArgumentException("Invalid bindings");
}
- // TODO: Runtime top-level APIs currently does not allow grouping overloads with
- // the function name. This capability will have to be added.
- if (overloadBindings.size() == 1) {
- CelFunctionBinding singleBinding = Iterables.getOnlyElement(overloadBindings);
- builder.addOverload(
- functionName,
- singleBinding.getArgTypes(),
- singleBinding.isStrict(),
- args -> guardedOp(functionName, args, singleBinding));
- } else {
- overloadBindings.forEach(
- overload ->
- builder.addOverload(
- overload.getOverloadId(),
- overload.getArgTypes(),
- overload.isStrict(),
- args -> guardedOp(functionName, args, overload)));
-
- // Setup dynamic dispatch
- CelFunctionOverload dynamicDispatchDef =
- args -> {
- for (CelFunctionBinding overload : overloadBindings) {
- if (CelResolvedOverload.canHandle(
- args, overload.getArgTypes(), overload.isStrict())) {
- return overload.getDefinition().apply(args);
- }
- }
-
- throw new IllegalArgumentException(
- "No matching overload for function: " + functionName);
- };
-
- boolean allOverloadsStrict = overloadBindings.stream().allMatch(CelFunctionBinding::isStrict);
- builder.addOverload(
- functionName, ImmutableList.of(), /* isStrict= */ allOverloadsStrict, dynamicDispatchDef);
- }
- }
-
- /** Creates an invocation guard around the overload definition. */
- private static Object guardedOp(
- String functionName, Object[] args, CelFunctionBinding singleBinding)
- throws CelEvaluationException {
- if (!CelResolvedOverload.canHandle(
- args, singleBinding.getArgTypes(), singleBinding.isStrict())) {
- throw new IllegalArgumentException("No matching overload for function: " + functionName);
- }
- return singleBinding.getDefinition().apply(args);
+ overloadBindings.forEach(
+ overload ->
+ builder.addOverload(
+ overload.getOverloadId(),
+ overload.getArgTypes(),
+ overload.isStrict(),
+ overload.getDefinition()));
}
@TestParameter boolean isParseOnly;
@@ -379,6 +326,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{}");
@@ -500,12 +458,21 @@ public void plan_call_mapIndex() throws Exception {
public void plan_call_noMatchingOverload_throws() throws Exception {
CelAbstractSyntaxTree ast = compile("concat(b'abc', dyn_var)");
Program program = PLANNER.plan(ast);
+ String errorMsg;
+ if (isParseOnly) {
+ errorMsg =
+ "No matching overload for function 'concat'. Overload candidates: concat_bytes_bytes,"
+ + " bytes_concat_bytes";
+ } else {
+ errorMsg = "No matching overload for function 'concat_bytes_bytes'";
+ }
CelEvaluationException e =
assertThrows(
CelEvaluationException.class,
() -> program.eval(ImmutableMap.of("dyn_var", "Impossible Overload")));
- assertThat(e).hasMessageThat().contains("No matching overload for function: concat");
+
+ assertThat(e).hasMessageThat().contains(errorMsg);
}
@Test
@@ -617,6 +584,23 @@ public void plan_call_withContainer(String expression) throws Exception {
assertThat(result).isEqualTo(8);
}
+ @Test
+ public void plan_call_lateBoundFunction() throws Exception {
+ CelAbstractSyntaxTree ast = compile("late_bound_func('test')");
+
+ Program program = PLANNER.plan(ast);
+
+ String result =
+ (String)
+ program.eval(
+ ImmutableMap.of(),
+ CelLateFunctionBindings.from(
+ CelFunctionBinding.from(
+ "late_bound_func_overload", String.class, (arg) -> arg + "_resolved")));
+
+ assertThat(result).isEqualTo("test_resolved");
+ }
+
@Test
public void plan_select_protoMessageField() throws Exception {
CelAbstractSyntaxTree ast = compile("msg.single_string");
@@ -818,7 +802,8 @@ public void plan_comprehension_iterationLimit_throws(String expression) throws E
newDispatcher(),
CEL_VALUE_CONVERTER,
CEL_CONTAINER,
- options);
+ options,
+ ImmutableSet.of());
CelAbstractSyntaxTree ast = compile(expression);
Program program = planner.plan(ast);
@@ -838,7 +823,8 @@ public void plan_comprehension_iterationLimit_success() throws Exception {
newDispatcher(),
CEL_VALUE_CONVERTER,
CEL_CONTAINER,
- options);
+ options,
+ ImmutableSet.of());
CelAbstractSyntaxTree ast = compile("[1, 2, 3].map(x, [1, 2].map(y, x + y))");
Program program = planner.plan(ast);
@@ -871,11 +857,6 @@ private static CelByteString concatenateByteArrays(CelByteString bytes1, CelByte
return bytes1.concat(bytes2);
}
- private static ImmutableSet fromStandardFunction(
- CelStandardFunction standardFunction) {
- return standardFunction.newFunctionBindings(CEL_OPTIONS, RUNTIME_EQUALITY);
- }
-
@SuppressWarnings("ImmutableEnumChecker") // Test only
private enum ConstantTestCase {
NULL("null", NullValue.NULL_VALUE),
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