diff --git a/.bazelrc b/.bazelrc
index 31bb2d7bc..968597053 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,6 +1,14 @@
common --enable_bzlmod
+# Use built-in protoc
+common --incompatible_enable_proto_toolchain_resolution --@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc
+
build --java_runtime_version=remotejdk_11
build --java_language_version=11
+
# Hide Java 8 deprecation warnings.
common --javacopt=-Xlint:-options
+
+# Remove flag once https://github.com/google/cel-spec/issues/508 and rules_jvm_external is fixed.
+common --incompatible_autoload_externally=proto_library,cc_proto_library,java_proto_library,java_test
+
diff --git a/.github/workflows/cross_artifact_dependencies_check.sh b/.github/workflows/cross_artifact_dependencies_check.sh
new file mode 100755
index 000000000..0802a299a
--- /dev/null
+++ b/.github/workflows/cross_artifact_dependencies_check.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+# 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.
+
+set -o pipefail
+
+TARGETS=(
+ "//publish:cel"
+ "//publish:cel_common"
+ "//publish:cel_compiler"
+ "//publish:cel_runtime"
+ "//publish:cel_protobuf"
+ "//publish:cel_v1alpha1"
+)
+
+echo "------------------------------------------------"
+echo "Checking for duplicates..."
+echo "------------------------------------------------"
+
+JDK8_FLAGS="--java_language_version=8 --java_runtime_version=8"
+bazel build $JDK8_FLAGS "${TARGETS[@]}" || { echo "Bazel build failed"; exit 1; }
+
+(
+ for target in "${TARGETS[@]}"; do
+ # Locate the jar
+ jar_path=$(bazel cquery "$target" --output=files 2>/dev/null | grep '\-project.jar$')
+
+ if [[ -z "$jar_path" ]]; then
+ echo "Error: Could not find -project.jar for target $target" >&2
+ exit 1
+ fi
+
+ # Fix relative paths if running from a subdir.
+ if [[ ! -f "$jar_path" ]]; then
+ if [[ -f "../../$jar_path" ]]; then
+ jar_path="../../$jar_path"
+ else
+ echo "Error: File not found at $jar_path" >&2
+ exit 1
+ fi
+ fi
+
+ echo "Inspecting: $target" >&2
+
+ # Extract classes and append the target name to the end of the line
+ # Format: dev/cel/expr/Expr.class //publish:cel_compiler
+ jar tf "$jar_path" | grep "\.class$" | awk -v tgt="$target" '{print $0, tgt}'
+ done
+) | awk '
+ # $1 is the Class Name, $2 is the Target Name
+ seen[$1] {
+ print "❌ DUPLICATE FOUND: " $1
+ print " Present in: " seen[$1]
+ print " And in: " $2
+ dupe=1
+ next
+ }
+ { seen[$1] = $2 }
+
+ END { if (dupe) exit 2 }
+'
+
+EXIT_CODE=$?
+
+if [ $EXIT_CODE -eq 0 ]; then
+ echo "✅ Success: No duplicate classes found."
+elif [ $EXIT_CODE -eq 2 ]; then
+ echo "⛔ Failure: Duplicate classes detected."
+else
+ echo "💥 Error: An unexpected error occurred (e.g., missing jar files). Exit Code: $EXIT_CODE"
+fi
+
+exit $EXIT_CODE
+
diff --git a/.github/workflows/unwanted_deps.sh b/.github/workflows/unwanted_deps.sh
index abef91a5f..c483f9730 100755
--- a/.github/workflows/unwanted_deps.sh
+++ b/.github/workflows/unwanted_deps.sh
@@ -36,10 +36,14 @@ checkUnwantedDeps '//publish:cel_runtime' '@cel_spec'
checkUnwantedDeps '//publish:cel_runtime' 'protobuf_java_util'
checkUnwantedDeps '//publish:cel' 'protobuf_java_util'
+# cel_runtime shouldn't depend on antlr
+checkUnwantedDeps '//publish:cel_runtime' '@maven//:org_antlr_antlr4_runtime'
+
# cel_runtime shouldn't depend on the protobuf_lite runtime
checkUnwantedDeps '//publish:cel_runtime' '@maven_android//:com_google_protobuf_protobuf_javalite'
checkUnwantedDeps '//publish:cel' '@maven_android//:com_google_protobuf_protobuf_javalite'
-# cel_runtime_android shouldn't depend on the full protobuf runtime
+# cel_runtime_android shouldn't depend on the full protobuf runtime or antlr
checkUnwantedDeps '//publish:cel_runtime_android' '@maven//:com_google_protobuf_protobuf_java'
+checkUnwantedDeps '//publish:cel_runtime_android' '@maven//:org_antlr_antlr4_runtime'
exit 0
diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index 6f61e9e22..4b206e3ec 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -15,6 +15,31 @@ concurrency:
cancel-in-progress: true
jobs:
+ Static-Checks:
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ steps:
+ - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
+ - run: echo "🐧 Job is running on a ${{ runner.os }} server!"
+ - run: echo "🔎 The name of your branch is ${GITHUB_REF} and your repository is ${{ github.repository }}."
+ - name: Check out repository code
+ uses: actions/checkout@v6
+ - name: Setup Bazel
+ uses: bazel-contrib/setup-bazel@0.18.0
+ with:
+ # Avoid downloading Bazel every time.
+ bazelisk-cache: true
+ # Store build cache per workflow.
+ disk-cache: ${{ github.workflow }}
+ # Share repository cache between workflows.
+ repository-cache: true
+ # Never write to the cache, strictly read-only
+ cache-save: false
+ - name: Unwanted Dependencies
+ run: .github/workflows/unwanted_deps.sh
+ - name: Cross-artifact Duplicate Classes Check
+ run: .github/workflows/cross_artifact_dependencies_check.sh
+ - run: echo "🍏 This job's status is ${{ job.status }}."
Bazel-Tests:
runs-on: ubuntu-latest
timeout-minutes: 30
@@ -23,9 +48,9 @@ jobs:
- run: echo "🐧 Job is running on a ${{ runner.os }} server!"
- run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
- name: Check out repository code
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup Bazel
- uses: bazel-contrib/setup-bazel@0.14.0
+ uses: bazel-contrib/setup-bazel@0.18.0
with:
# Avoid downloading Bazel every time.
bazelisk-cache: true
@@ -33,6 +58,8 @@ jobs:
disk-cache: ${{ github.workflow }}
# Share repository cache between workflows.
repository-cache: true
+ # Prevent PRs from polluting cache
+ cache-save: ${{ github.event_name != 'pull_request' }}
- name: Bazel Output Version
run: bazelisk --version
- name: Java 8 Build
@@ -41,19 +68,41 @@ jobs:
# Exclude codelab exercises as they are intentionally made to fail
# Exclude maven conformance tests. They are only executed when there's version change.
run: bazelisk test ... --deleted_packages=//codelab/src/test/codelab --test_output=errors --test_tag_filters=-conformance_maven --build_tag_filters=-conformance_maven
+ - run: echo "🍏 This job's status is ${{ job.status }}."
- # -- Start of Maven Conformance Tests (Ran only when there's version changes) --
- - name: Get changed file
+ # -- Start of Maven Conformance Tests (Ran only when there's version changes) --
+ Maven-Conformance:
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ steps:
+ - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
+ - run: echo "🐧 Job is running on a ${{ runner.os }} server!"
+ - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
+ - name: Check out repository code
+ uses: actions/checkout@v6
+ - name: Get changed files
id: changed_file
- uses: tj-actions/changed-files@v44
+ uses: tj-actions/changed-files@v46
with:
files: publish/cel_version.bzl
+ - name: Setup Bazel
+ if: steps.changed_file.outputs.any_changed == 'true'
+ uses: bazel-contrib/setup-bazel@0.18.0
+ with:
+ # Avoid downloading Bazel every time.
+ bazelisk-cache: true
+ # Store build cache per workflow.
+ disk-cache: ${{ github.workflow }}
+ # Share repository cache between workflows.
+ repository-cache: true
+ # Never write to the cache, strictly read-only
+ cache-save: false
- name: Verify Version Consistency
if: steps.changed_file.outputs.any_changed == 'true'
run: |
CEL_VERSION=$(grep 'CEL_VERSION =' publish/cel_version.bzl | cut -d '"' -f 2)
- MODULE_VERSION=$(grep 'dev.cel:cel' MODULE.bazel | cut -d '"' -f 2 | cut -d ':' -f 3)
+ MODULE_VERSION=$(grep 'CEL_VERSION =' MODULE.bazel | cut -d '"' -f 2)
if [ -z "$CEL_VERSION" ] || [ -z "$MODULE_VERSION" ]; then
echo "❌ Error: Could not extract one or both version strings."
@@ -72,8 +121,6 @@ jobs:
- name: Run Conformance Maven Test on Version Change
if: steps.changed_file.outputs.any_changed == 'true'
run: bazelisk test //conformance/src/test/java/dev/cel/conformance:conformance_maven --test_output=errors
- # -- End of Maven Conformance Tests --
-
- - name: Unwanted Dependencies
- run: .github/workflows/unwanted_deps.sh
- - run: echo "🍏 This job's status is ${{ job.status }}."
+ - run: echo "🍏 This job's status is ${JOB_STATUS}."
+ env:
+ JOB_STATUS: ${{ job.status }}
diff --git a/.gitignore b/.gitignore
index e8052913c..3a2e0015d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,5 @@ mvn-artifacts
*.swp
*.lock
+.eclipse
+.vscode
diff --git a/BUILD.bazel b/BUILD.bazel
index 06942bc50..024908625 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -22,6 +22,13 @@ load(
"DEFAULT_TOOLCHAIN_CONFIGURATION",
"default_java_toolchain",
)
+load(
+ "@rules_java//java:defs.bzl",
+ "java_binary",
+ "java_library",
+ "java_package_configuration",
+ "java_plugin",
+)
load("@rules_license//rules:license.bzl", "license")
licenses(["notice"]) # Apache License 2.0
diff --git a/MODULE.bazel b/MODULE.bazel
index 8e0e79068..0b67c825c 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -16,18 +16,23 @@ module(
name = "cel_java",
)
-bazel_dep(name = "bazel_skylib", version = "1.7.1")
-bazel_dep(name = "rules_jvm_external", version = "6.7")
-bazel_dep(name = "protobuf", version = "29.3", repo_name = "com_google_protobuf") # see https://github.com/bazelbuild/rules_android/issues/373
-bazel_dep(name = "googleapis", version = "0.0.0-20241220-5e258e33.bcr.1", repo_name = "com_google_googleapis")
-bazel_dep(name = "rules_pkg", version = "1.0.1")
+bazel_dep(name = "bazel_skylib", version = "1.9.0")
+bazel_dep(name = "rules_jvm_external", version = "6.10")
+bazel_dep(name = "protobuf", version = "33.4", repo_name = "com_google_protobuf") # see https://github.com/bazelbuild/rules_android/issues/373
+bazel_dep(name = "googleapis", version = "0.0.0-20260223-edfe7983", repo_name = "com_google_googleapis")
+bazel_dep(name = "rules_pkg", version = "1.2.0")
bazel_dep(name = "rules_license", version = "1.0.0")
bazel_dep(name = "rules_proto", version = "7.1.0")
-bazel_dep(name = "rules_java", version = "8.12.0")
-bazel_dep(name = "rules_android", version = "0.6.4")
-bazel_dep(name = "rules_shell", version = "0.5.1")
+bazel_dep(name = "rules_java", version = "9.3.0")
+bazel_dep(name = "rules_android", version = "0.7.1")
+bazel_dep(name = "rules_shell", version = "0.6.1")
bazel_dep(name = "googleapis-java", version = "1.0.0")
-bazel_dep(name = "cel-spec", version = "0.24.0", repo_name = "cel_spec")
+bazel_dep(name = "cel-spec", version = "0.25.1", repo_name = "cel_spec")
+bazel_dep(name = "rules_go", version = "0.50.1")
+
+# Required by cel-spec to satisfy gazelle transitive dependency
+go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
+go_sdk.download(version = "1.23.0")
switched_rules = use_extension("@com_google_googleapis//:extensions.bzl", "switched_rules")
switched_rules.use_languages(java = True)
@@ -39,7 +44,9 @@ GUAVA_VERSION = "33.5.0"
TRUTH_VERSION = "1.4.4"
-PROTOBUF_JAVA_VERSION = "4.33.0"
+PROTOBUF_JAVA_VERSION = "4.33.5"
+
+CEL_VERSION = "0.12.0"
# Compile only artifacts
[
@@ -114,7 +121,11 @@ maven.install(
maven.install(
name = "maven_conformance",
- artifacts = ["dev.cel:cel:0.11.1"],
+ artifacts = [
+ "dev.cel:cel:" + CEL_VERSION,
+ "dev.cel:compiler:" + CEL_VERSION,
+ "dev.cel:runtime:" + CEL_VERSION,
+ ],
repositories = [
"https://maven.google.com",
"https://repo1.maven.org/maven2",
diff --git a/README.md b/README.md
index f46a1f8c6..40bd9deac 100644
--- a/README.md
+++ b/README.md
@@ -55,14 +55,14 @@ CEL-Java is available in Maven Central Repository. [Download the JARs here][8] o
dev.celcel
- 0.11.1
+ 0.12.0
```
**Gradle**
```gradle
-implementation 'dev.cel:cel:0.11.1'
+implementation 'dev.cel:cel:0.12.0'
```
Then run this example:
diff --git a/bundle/BUILD.bazel b/bundle/BUILD.bazel
index 7f21cf219..11e0b8a6d 100644
--- a/bundle/BUILD.bazel
+++ b/bundle/BUILD.bazel
@@ -7,7 +7,18 @@ package(
java_library(
name = "cel",
- exports = ["//bundle/src/main/java/dev/cel/bundle:cel"],
+ exports = [
+ "//bundle/src/main/java/dev/cel/bundle:cel",
+ "//bundle/src/main/java/dev/cel/bundle:cel_factory",
+ ],
+)
+
+java_library(
+ name = "cel_experimental_factory",
+ visibility = ["//:internal"],
+ exports = [
+ "//bundle/src/main/java/dev/cel/bundle:cel_experimental_factory",
+ ],
)
java_library(
@@ -27,6 +38,12 @@ java_library(
java_library(
name = "environment_exporter",
- visibility = ["//:internal"],
exports = ["//bundle/src/main/java/dev/cel/bundle:environment_exporter"],
)
+
+java_library(
+ name = "cel_impl",
+ testonly = 1,
+ visibility = ["//:internal"],
+ exports = ["//bundle/src/main/java/dev/cel/bundle:cel_impl"],
+)
diff --git a/bundle/src/main/java/dev/cel/bundle/BUILD.bazel b/bundle/src/main/java/dev/cel/bundle/BUILD.bazel
index 50b78c63b..822511e4c 100644
--- a/bundle/src/main/java/dev/cel/bundle/BUILD.bazel
+++ b/bundle/src/main/java/dev/cel/bundle/BUILD.bazel
@@ -11,8 +11,6 @@ package(
CEL_SOURCES = [
"Cel.java",
"CelBuilder.java",
- "CelFactory.java",
- "CelImpl.java",
]
java_library(
@@ -21,11 +19,72 @@ java_library(
tags = [
],
deps = [
+ "//checker:checker_legacy_environment",
+ "//checker:proto_type_mask",
+ "//checker:standard_decl",
+ "//common:compiler_common",
+ "//common:container",
+ "//common:options",
+ "//common/types:type_providers",
+ "//common/values:cel_value_provider",
+ "//compiler:compiler_builder",
+ "//parser:macro",
+ "//runtime",
+ "//runtime:function_binding",
+ "//runtime:standard_functions",
+ "@cel_spec//proto/cel/expr:checked_java_proto",
+ "@maven//:com_google_code_findbugs_annotations",
+ "@maven//:com_google_errorprone_error_prone_annotations",
+ "@maven//:com_google_guava_guava",
+ "@maven//:com_google_protobuf_protobuf_java",
+ ],
+)
+
+java_library(
+ name = "cel_factory",
+ srcs = ["CelFactory.java"],
+ tags = [
+ ],
+ deps = [
+ ":cel",
+ ":cel_impl",
"//checker",
+ "//common:options",
+ "//compiler",
+ "//compiler:compiler_builder",
+ "//parser",
+ "//runtime",
+ ],
+)
+
+java_library(
+ name = "cel_experimental_factory",
+ srcs = ["CelExperimentalFactory.java"],
+ tags = [
+ ],
+ deps = [
+ ":cel",
+ ":cel_impl",
+ "//checker",
+ "//common:options",
+ "//common/annotations",
+ "//compiler",
+ "//parser",
+ "//runtime:runtime_planner_impl",
+ ],
+)
+
+java_library(
+ name = "cel_impl",
+ srcs = ["CelImpl.java"],
+ tags = [
+ ],
+ deps = [
+ ":cel",
"//checker:checker_builder",
- "//checker:checker_legacy_environment",
"//checker:proto_type_mask",
"//checker:standard_decl",
+ "//checker:type_provider_legacy",
"//common:cel_ast",
"//common:cel_source",
"//common:compiler_common",
@@ -36,16 +95,14 @@ java_library(
"//common/types:cel_proto_types",
"//common/types:type_providers",
"//common/values:cel_value_provider",
- "//compiler",
"//compiler:compiler_builder",
- "//parser",
"//parser:macro",
"//parser:parser_builder",
"//runtime",
"//runtime:function_binding",
+ "//runtime:runtime_planner_impl",
"//runtime:standard_functions",
"@cel_spec//proto/cel/expr:checked_java_proto",
- "@maven//:com_google_code_findbugs_annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
"@maven//:com_google_protobuf_protobuf_java",
@@ -60,6 +117,7 @@ java_library(
tags = [
],
deps = [
+ ":cel_factory",
":environment_exception",
":required_fields_checker",
"//:auto_value",
@@ -73,7 +131,6 @@ java_library(
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
- "//extensions:optional_library",
"//parser:macro",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
@@ -103,6 +160,7 @@ java_library(
":environment",
":environment_exception",
"//common:compiler_common",
+ "//common:container",
"//common/formats:file_source",
"//common/formats:parser_context",
"//common/formats:yaml_helper",
@@ -126,6 +184,7 @@ java_library(
":environment",
"//:auto_value",
"//bundle:cel",
+ "//checker:checker_builder",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
@@ -133,6 +192,7 @@ java_library(
"//common/types:cel_proto_types",
"//common/types:cel_types",
"//common/types:type_providers",
+ "//compiler:compiler_builder",
"//extensions",
"//extensions:extension_library",
"//parser:macro",
diff --git a/bundle/src/main/java/dev/cel/bundle/CelEnvironment.java b/bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
index 9f33032a8..8614b87b5 100644
--- a/bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
+++ b/bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
@@ -48,10 +48,12 @@
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelStandardMacro;
+import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.Optional;
+import java.util.function.ObjIntConsumer;
/**
* CelEnvironment is a native representation of a CEL environment for compiler and runtime. This
@@ -73,6 +75,24 @@ public abstract class CelEnvironment {
"strings", CanonicalCelExtension.STRINGS,
"comprehensions", CanonicalCelExtension.COMPREHENSIONS);
+ private static final ImmutableMap> LIMIT_HANDLERS =
+ ImmutableMap.of(
+ "cel.limit.expression_code_points",
+ (options, value) -> options.maxExpressionCodePointSize(value),
+ "cel.limit.parse_error_recovery",
+ (options, value) -> options.maxParseErrorRecoveryLimit(value),
+ "cel.limit.parse_recursion_depth",
+ (options, value) -> options.maxParseRecursionDepth(value));
+
+ private static final ImmutableMap FEATURE_HANDLERS =
+ ImmutableMap.of(
+ "cel.feature.macro_call_tracking",
+ (options, enabled) -> options.populateMacroCalls(enabled),
+ "cel.feature.backtick_escape_syntax",
+ (options, enabled) -> options.enableQuotedIdentifierSyntax(enabled),
+ "cel.feature.cross_type_numeric_comparisons",
+ (options, enabled) -> options.enableHeterogeneousNumericComparisons(enabled));
+
/** Environment source in textual format (ex: textproto, YAML). */
public abstract Optional source();
@@ -80,10 +100,9 @@ public abstract class CelEnvironment {
public abstract String name();
/**
- * An optional description of the config (example: location of the file containing the config
- * content).
+ * Container, which captures default namespace and aliases for value resolution.
*/
- public abstract String container();
+ public abstract CelContainer container();
/**
* An optional description of the environment (example: location of the file containing the config
@@ -109,6 +128,12 @@ public abstract class CelEnvironment {
/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional standardLibrarySubset();
+ /** Feature flags to enable in the environment. */
+ public abstract ImmutableSet features();
+
+ /** Limits to set in the environment. */
+ public abstract ImmutableSet limits();
+
/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
@@ -124,7 +149,12 @@ public abstract static class Builder {
public abstract Builder setDescription(String description);
- public abstract Builder setContainer(String container);
+ public abstract Builder setContainer(CelContainer container);
+
+ @CanIgnoreReturnValue
+ public Builder setContainer(String container) {
+ return setContainer(CelContainer.ofName(container));
+ }
@CanIgnoreReturnValue
public Builder addExtensions(ExtensionConfig... extensions) {
@@ -155,6 +185,20 @@ public Builder setFunctions(FunctionDecl... functions) {
public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);
+ @CanIgnoreReturnValue
+ public Builder setFeatures(FeatureFlag... featureFlags) {
+ return setFeatures(ImmutableSet.copyOf(featureFlags));
+ }
+
+ public abstract Builder setFeatures(ImmutableSet featureFlags);
+
+ @CanIgnoreReturnValue
+ public Builder setLimits(Limit... limits) {
+ return setLimits(ImmutableSet.copyOf(limits));
+ }
+
+ public abstract Builder setLimits(ImmutableSet limits);
+
abstract CelEnvironment autoBuild();
@CheckReturnValue
@@ -182,21 +226,25 @@ public static Builder newBuilder() {
return new AutoValue_CelEnvironment.Builder()
.setName("")
.setDescription("")
- .setContainer("")
+ .setContainer(CelContainer.ofName(""))
.setVariables(ImmutableSet.of())
- .setFunctions(ImmutableSet.of());
+ .setFunctions(ImmutableSet.of())
+ .setFeatures(ImmutableSet.of())
+ .setLimits(ImmutableSet.of());
}
/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
+ celOptions = applyEnvironmentOptions(celOptions);
try {
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
+ .setContainer(container())
+ .setOptions(celOptions)
.setTypeProvider(celTypeProvider)
- .setContainer(CelContainer.ofName(container()))
.addVarDeclarations(
variables().stream()
.map(v -> v.toCelVarDecl(celTypeProvider))
@@ -206,10 +254,6 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
.map(f -> f.toCelFunctionDecl(celTypeProvider))
.collect(toImmutableList()));
- if (!container().isEmpty()) {
- compilerBuilder.setContainer(CelContainer.ofName(container()));
- }
-
addAllCompilerExtensions(compilerBuilder, celOptions);
applyStandardLibrarySubset(compilerBuilder);
@@ -222,19 +266,39 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
/** Extends the provided {@link Cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
+ celOptions = applyEnvironmentOptions(celOptions);
try {
// Casting is necessary to only extend the compiler here
CelCompiler celCompiler = extend((CelCompiler) cel, celOptions);
- CelRuntimeBuilder celRuntimeBuilder = cel.toRuntimeBuilder();
- addAllRuntimeExtensions(celRuntimeBuilder, celOptions);
+ CelRuntime celRuntime = extendRuntime(cel, celOptions);
- return CelFactory.combine(celCompiler, celRuntimeBuilder.build());
+ return CelFactory.combine(celCompiler, celRuntime);
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}
+ private CelOptions applyEnvironmentOptions(CelOptions celOptions) {
+ CelOptions.Builder optionsBuilder = celOptions.toBuilder();
+ for (FeatureFlag featureFlag : features()) {
+ BooleanOptionConsumer consumer = FEATURE_HANDLERS.get(featureFlag.name());
+ if (consumer == null) {
+ throw new IllegalArgumentException("Unknown feature flag: " + featureFlag.name());
+ }
+ consumer.accept(optionsBuilder, featureFlag.enabled());
+ }
+ for (Limit limit : limits()) {
+ int value = limit.value() < 0 ? -1 : limit.value();
+ ObjIntConsumer consumer = LIMIT_HANDLERS.get(limit.name());
+ if (consumer == null) {
+ throw new IllegalArgumentException("Unknown limit: " + limit.name());
+ }
+ consumer.accept(optionsBuilder, value);
+ }
+ return optionsBuilder.build();
+ }
+
private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
@@ -250,7 +314,9 @@ private void addAllCompilerExtensions(
}
}
- private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
+ private CelRuntime extendRuntime(CelRuntime celRuntime, CelOptions celOptions) {
+ CelRuntimeBuilder celRuntimeBuilder = celRuntime.toRuntimeBuilder();
+ celRuntimeBuilder.setOptions(celOptions);
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
@@ -262,6 +328,7 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
+ return celRuntimeBuilder.build();
}
private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
@@ -605,6 +672,10 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
return TypeParamType.create(name());
}
+ if (name().equals("dyn")) {
+ return SimpleType.DYN;
+ }
+
CelType simpleType = SimpleType.findByName(name()).orElse(null);
if (simpleType != null) {
return simpleType;
@@ -625,6 +696,39 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
}
}
+ /** Represents a feature flag that can be enabled in the environment. */
+ @AutoValue
+ public abstract static class FeatureFlag {
+ /** Normalized name of the feature flag. */
+ public abstract String name();
+
+ /** Whether the feature is enabled or disabled. */
+ public abstract boolean enabled();
+
+ public static FeatureFlag create(String name, boolean enabled) {
+ return new AutoValue_CelEnvironment_FeatureFlag(name, enabled);
+ }
+ }
+
+ /**
+ * Represents a configurable limit in the environment.
+ *
+ *
A negative value indicates no limit. If not specified, the limit should be set to the
+ * library default.
+ */
+ @AutoValue
+ public abstract static class Limit {
+ /** Normalized name of the limit (e.g. cel.limit.expression_code_points */
+ public abstract String name();
+
+ /** The value of the limit, -1 means no limit. */
+ public abstract int value();
+
+ public static Limit create(String name, int value) {
+ return new AutoValue_CelEnvironment_Limit(name, value);
+ }
+ }
+
/**
* Represents a configuration for a canonical CEL extension that can be enabled in the
* environment.
@@ -683,6 +787,38 @@ public static ExtensionConfig latest(String name) {
}
}
+ @AutoValue
+ abstract static class Alias {
+ abstract String alias();
+
+ abstract String qualifiedName();
+
+ static Builder newBuilder() {
+ return new AutoValue_CelEnvironment_Alias.Builder();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder implements RequiredFieldsChecker {
+
+ abstract Optional alias();
+
+ abstract Optional qualifiedName();
+
+ abstract Builder setAlias(String alias);
+
+ abstract Builder setQualifiedName(String qualifiedName);
+
+ abstract Alias build();
+
+ @Override
+ public ImmutableList requiredFields() {
+ return ImmutableList.of(
+ RequiredField.of("alias", this::alias),
+ RequiredField.of("qualified_name", this::qualifiedName));
+ }
+ }
+ }
+
@VisibleForTesting
enum CanonicalCelExtension {
BINDINGS((options, version) -> CelExtensions.bindings()),
@@ -916,4 +1052,9 @@ public static OverloadSelector.Builder newBuilder() {
}
}
}
+
+ @FunctionalInterface
+ private static interface BooleanOptionConsumer {
+ void accept(CelOptions.Builder options, boolean value);
+ }
}
diff --git a/bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java b/bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
index d303e0528..f86787090 100644
--- a/bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
+++ b/bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
@@ -22,6 +22,7 @@
import dev.cel.expr.Decl.FunctionDecl;
import dev.cel.expr.Decl.FunctionDecl.Overload;
import com.google.auto.value.AutoValue;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
@@ -30,6 +31,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.bundle.CelEnvironment.OverloadDecl;
+import dev.cel.checker.CelCheckerBuilder;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardIdentifier;
import dev.cel.common.CelFunctionDecl;
@@ -41,6 +43,7 @@
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypes;
+import dev.cel.compiler.CelCompiler;
import dev.cel.extensions.CelExtensionLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelMacro;
@@ -161,6 +164,24 @@ public static CelEnvironmentExporter.Builder newBuilder() {
*
*/
public CelEnvironment export(Cel cel) {
+ return export((CelCompiler) cel);
+ }
+
+ /**
+ * Exports a {@link CelEnvironment} that describes the configuration of the given {@link
+ * CelCompiler} instance.
+ *
+ *
The exported environment includes:
+ *
+ *
+ *
Standard library subset: functions and their overloads that are either included or
+ * excluded from the standard library.
+ *
Extension libraries: names and versions of the extension libraries that are used.
+ *
Custom declarations: functions and variables that are not part of the standard library or
+ * any of the extension libraries.
+ *
+ */
+ public CelEnvironment export(CelCompiler cel) {
// Inventory is a full set of declarations and macros that are found in the configuration of
// the supplied CEL instance.
//
@@ -169,21 +190,62 @@ public CelEnvironment export(Cel cel) {
//
// Whatever is left will be included in the Environment as custom declarations.
+ // Checker builder is used to access some parts of the config not exposed in the EnvVisitable
+ // interface.
+ CelCheckerBuilder checkerBuilder = cel.toCheckerBuilder();
+
+ CelEnvironment.Builder envBuilder =
+ CelEnvironment.newBuilder().setContainer(checkerBuilder.container());
+ addOptions(envBuilder, checkerBuilder.options());
+
Set