Skip to content

Commit 0268902

Browse files
l46kokcopybara-github
authored andcommitted
Move Optional function bindings from standard lib to CelOptionalLibrary
PiperOrigin-RevId: 769775644
1 parent 65a4ca2 commit 0268902

9 files changed

Lines changed: 128 additions & 241 deletions

File tree

extensions/src/main/java/dev/cel/extensions/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ java_library(
155155
deps = [
156156
"//checker:checker_builder",
157157
"//common:compiler_common",
158+
"//common:options",
158159
"//common/ast",
159160
"//common/types",
160161
"//compiler:compiler_builder",
@@ -163,6 +164,7 @@ java_library(
163164
"//parser:parser_builder",
164165
"//runtime",
165166
"//runtime:function_binding",
167+
"//runtime:runtime_equality",
166168
"@maven//:com_google_guava_guava",
167169
"@maven//:com_google_protobuf_protobuf_java",
168170
"@maven_android//:com_google_protobuf_protobuf_javalite",

extensions/src/main/java/dev/cel/extensions/CelOptionalLibrary.java

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static com.google.common.collect.ImmutableList.toImmutableList;
2020

2121
import com.google.common.collect.ImmutableList;
22+
import com.google.common.primitives.Ints;
2223
import com.google.common.primitives.UnsignedLong;
2324
import com.google.protobuf.ByteString;
2425
import com.google.protobuf.Duration;
@@ -28,6 +29,7 @@
2829
import dev.cel.checker.CelCheckerBuilder;
2930
import dev.cel.common.CelFunctionDecl;
3031
import dev.cel.common.CelIssue;
32+
import dev.cel.common.CelOptions;
3133
import dev.cel.common.CelOverloadDecl;
3234
import dev.cel.common.CelVarDecl;
3335
import dev.cel.common.ast.CelExpr;
@@ -43,14 +45,16 @@
4345
import dev.cel.parser.CelParserBuilder;
4446
import dev.cel.parser.Operator;
4547
import dev.cel.runtime.CelFunctionBinding;
48+
import dev.cel.runtime.CelInternalRuntimeLibrary;
4649
import dev.cel.runtime.CelRuntimeBuilder;
47-
import dev.cel.runtime.CelRuntimeLibrary;
50+
import dev.cel.runtime.RuntimeEquality;
4851
import java.util.Collection;
52+
import java.util.List;
4953
import java.util.Map;
5054
import java.util.Optional;
5155

5256
/** Internal implementation of CEL optional values. */
53-
public final class CelOptionalLibrary implements CelCompilerLibrary, CelRuntimeLibrary {
57+
public final class CelOptionalLibrary implements CelCompilerLibrary, CelInternalRuntimeLibrary {
5458
public static final CelOptionalLibrary INSTANCE = new CelOptionalLibrary();
5559

5660
/** Enumerations of function names used for supporting optionals. */
@@ -171,8 +175,14 @@ public void setCheckerOptions(CelCheckerBuilder checkerBuilder) {
171175
}
172176

173177
@Override
174-
@SuppressWarnings("unchecked")
175178
public void setRuntimeOptions(CelRuntimeBuilder runtimeBuilder) {
179+
throw new UnsupportedOperationException("Unsupported");
180+
}
181+
182+
@SuppressWarnings({"unchecked", "rawtypes"})
183+
@Override
184+
public void setRuntimeOptions(
185+
CelRuntimeBuilder runtimeBuilder, RuntimeEquality runtimeEquality, CelOptions celOptions) {
176186
runtimeBuilder.addFunctionBindings(
177187
CelFunctionBinding.from("optional_of", Object.class, Optional::of),
178188
CelFunctionBinding.from(
@@ -189,7 +199,48 @@ public void setRuntimeOptions(CelRuntimeBuilder runtimeBuilder) {
189199
CelFunctionBinding.from("optional_none", ImmutableList.of(), val -> Optional.empty()),
190200
CelFunctionBinding.from("optional_value", Object.class, val -> ((Optional<?>) val).get()),
191201
CelFunctionBinding.from(
192-
"optional_hasValue", Object.class, val -> ((Optional<?>) val).isPresent()));
202+
"optional_hasValue", Object.class, val -> ((Optional<?>) val).isPresent()),
203+
CelFunctionBinding.from(
204+
"select_optional_field", // This only handles map selection. Proto selection is
205+
// special cased inside the interpreter.
206+
Map.class,
207+
String.class,
208+
runtimeEquality::findInMap),
209+
CelFunctionBinding.from(
210+
"map_optindex_optional_value", Map.class, Object.class, runtimeEquality::findInMap),
211+
CelFunctionBinding.from(
212+
"optional_map_optindex_optional_value",
213+
Optional.class,
214+
Object.class,
215+
(Optional optionalMap, Object key) ->
216+
indexOptionalMap(optionalMap, key, runtimeEquality)),
217+
CelFunctionBinding.from(
218+
"optional_map_index_value",
219+
Optional.class,
220+
Object.class,
221+
(Optional optionalMap, Object key) ->
222+
indexOptionalMap(optionalMap, key, runtimeEquality)),
223+
CelFunctionBinding.from(
224+
"optional_list_index_int",
225+
Optional.class,
226+
Long.class,
227+
CelOptionalLibrary::indexOptionalList),
228+
CelFunctionBinding.from(
229+
"list_optindex_optional_int",
230+
List.class,
231+
Long.class,
232+
(List list, Long index) -> {
233+
int castIndex = Ints.checkedCast(index);
234+
if (castIndex < 0 || castIndex >= list.size()) {
235+
return Optional.empty();
236+
}
237+
return Optional.of(list.get(castIndex));
238+
}),
239+
CelFunctionBinding.from(
240+
"optional_list_optindex_optional_int",
241+
Optional.class,
242+
Long.class,
243+
CelOptionalLibrary::indexOptionalList));
193244
}
194245

195246
private static ImmutableList<Object> elideOptionalCollection(Collection<Optional<Object>> list) {
@@ -298,5 +349,28 @@ private static Optional<CelExpr> expandOptFlatMap(
298349
exprFactory.newGlobalCall(Function.OPTIONAL_NONE.getFunction())));
299350
}
300351

352+
private static Object indexOptionalMap(
353+
Optional<?> optionalMap, Object key, RuntimeEquality runtimeEquality) {
354+
if (!optionalMap.isPresent()) {
355+
return Optional.empty();
356+
}
357+
358+
Map<?, ?> map = (Map<?, ?>) optionalMap.get();
359+
360+
return runtimeEquality.findInMap(map, key);
361+
}
362+
363+
private static Object indexOptionalList(Optional<?> optionalList, long index) {
364+
if (!optionalList.isPresent()) {
365+
return Optional.empty();
366+
}
367+
List<?> list = (List<?>) optionalList.get();
368+
int castIndex = Ints.checkedCast(index);
369+
if (castIndex < 0 || castIndex >= list.size()) {
370+
return Optional.empty();
371+
}
372+
return Optional.of(list.get(castIndex));
373+
}
374+
301375
private CelOptionalLibrary() {}
302376
}

runtime/src/main/java/dev/cel/runtime/BUILD.bazel

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ java_library(
451451

452452
# keep sorted
453453
RUNTIME_SOURCES = [
454+
"CelInternalRuntimeLibrary.java",
454455
"CelRuntime.java",
455456
"CelRuntimeBuilder.java",
456457
"CelRuntimeFactory.java",
@@ -627,7 +628,6 @@ java_library(
627628
"//runtime/standard:multiply",
628629
"//runtime/standard:negate",
629630
"//runtime/standard:not_equals",
630-
"//runtime/standard:optional",
631631
"//runtime/standard:size",
632632
"//runtime/standard:standard_function",
633633
"//runtime/standard:starts_with",
@@ -689,7 +689,6 @@ cel_android_library(
689689
"//runtime/standard:multiply_android",
690690
"//runtime/standard:negate_android",
691691
"//runtime/standard:not_equals_android",
692-
"//runtime/standard:optional_android",
693692
"//runtime/standard:size_android",
694693
"//runtime/standard:standard_function_android",
695694
"//runtime/standard:starts_with_android",
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.runtime;
16+
17+
import dev.cel.common.CelOptions;
18+
import dev.cel.common.annotations.Internal;
19+
20+
/**
21+
* CelInternalRuntimeLibrary defines the interface to extend functionalities beyond the CEL standard
22+
* functions for {@link CelRuntime}, with access to runtime internals. This is not intended for
23+
* general use.
24+
*
25+
* <p>CEL Library Internals. Do Not Use.
26+
*/
27+
@Internal
28+
public interface CelInternalRuntimeLibrary extends CelRuntimeLibrary {
29+
30+
/**
31+
* Configures the runtime to support the library implementation, such as adding function bindings.
32+
*/
33+
void setRuntimeOptions(
34+
CelRuntimeBuilder runtimeBuilder, RuntimeEquality runtimeEquality, CelOptions celOptions);
35+
}

runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,6 @@ public CelRuntimeLegacyImpl build() {
240240
+ " bindings.");
241241
}
242242

243-
ImmutableSet<CelRuntimeLibrary> runtimeLibraries = celRuntimeLibraries.build();
244-
// Add libraries, such as extensions
245-
runtimeLibraries.forEach(celLibrary -> celLibrary.setRuntimeOptions(this));
246-
247243
ImmutableSet<FileDescriptor> fileDescriptors = fileTypes.build();
248244
CelDescriptors celDescriptors =
249245
CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(
@@ -270,6 +266,17 @@ public CelRuntimeLegacyImpl build() {
270266
DynamicProto dynamicProto = DynamicProto.create(runtimeTypeFactory);
271267
RuntimeEquality runtimeEquality = ProtoMessageRuntimeEquality.create(dynamicProto, options);
272268

269+
ImmutableSet<CelRuntimeLibrary> runtimeLibraries = celRuntimeLibraries.build();
270+
// Add libraries, such as extensions
271+
for (CelRuntimeLibrary celLibrary : runtimeLibraries) {
272+
if (celLibrary instanceof CelInternalRuntimeLibrary) {
273+
((CelInternalRuntimeLibrary) celLibrary)
274+
.setRuntimeOptions(this, runtimeEquality, options);
275+
} else {
276+
celLibrary.setRuntimeOptions(this);
277+
}
278+
}
279+
273280
ImmutableMap.Builder<String, CelFunctionBinding> functionBindingsBuilder =
274281
ImmutableMap.builder();
275282
for (CelFunctionBinding standardFunctionBinding :

runtime/src/main/java/dev/cel/runtime/CelStandardFunctions.java

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@
9999
import dev.cel.runtime.standard.NegateOperator.NegateOverload;
100100
import dev.cel.runtime.standard.NotEqualsOperator;
101101
import dev.cel.runtime.standard.NotEqualsOperator.NotEqualsOverload;
102-
import dev.cel.runtime.standard.OptionalFunction;
103-
import dev.cel.runtime.standard.OptionalFunction.OptionalOverload;
104102
import dev.cel.runtime.standard.SizeFunction;
105103
import dev.cel.runtime.standard.SizeFunction.SizeOverload;
106104
import dev.cel.runtime.standard.StartsWithFunction;
@@ -155,7 +153,6 @@ public final class CelStandardFunctions {
155153
MultiplyOperator.create(),
156154
NegateOperator.create(),
157155
NotEqualsOperator.create(),
158-
OptionalFunction.create(),
159156
SizeFunction.create(),
160157
StartsWithFunction.create(),
161158
StringFunction.create(),
@@ -327,15 +324,7 @@ public enum StandardFunction {
327324
Comparison.GREATER_EQUALS_INT64_DOUBLE,
328325
Comparison.GREATER_EQUALS_DOUBLE_INT64,
329326
Comparison.GREATER_EQUALS_UINT64_DOUBLE,
330-
Comparison.GREATER_EQUALS_DOUBLE_UINT64),
331-
OPTIONAL(
332-
Overload.OptionalValue.SELECT_OPTIONAL_FIELD,
333-
Overload.OptionalValue.MAP_OPTINDEX_OPTIONAL_VALUE,
334-
Overload.OptionalValue.OPTIONAL_MAP_OPTINDEX_OPTIONAL_VALUE,
335-
Overload.OptionalValue.OPTIONAL_MAP_INDEX_VALUE,
336-
Overload.OptionalValue.OPTIONAL_LIST_INDEX_INT,
337-
Overload.OptionalValue.LIST_OPTINDEX_OPTIONAL_INT,
338-
Overload.OptionalValue.OPTIONAL_LIST_OPTINDEX_OPTIONAL_INT);
327+
Comparison.GREATER_EQUALS_DOUBLE_UINT64);
339328

340329
/** Container class for CEL standard function overloads. */
341330
public static final class Overload {
@@ -708,32 +697,6 @@ public boolean isHeterogeneousComparison() {
708697
}
709698
}
710699

711-
/** Overloads for optional values. */
712-
public enum OptionalValue implements StandardOverload {
713-
SELECT_OPTIONAL_FIELD(OptionalOverload.SELECT_OPTIONAL_FIELD::newFunctionBinding),
714-
MAP_OPTINDEX_OPTIONAL_VALUE(
715-
OptionalOverload.MAP_OPTINDEX_OPTIONAL_VALUE::newFunctionBinding),
716-
OPTIONAL_MAP_OPTINDEX_OPTIONAL_VALUE(
717-
OptionalOverload.OPTIONAL_MAP_OPTINDEX_OPTIONAL_VALUE::newFunctionBinding),
718-
OPTIONAL_MAP_INDEX_VALUE(OptionalOverload.OPTIONAL_MAP_INDEX_VALUE::newFunctionBinding),
719-
OPTIONAL_LIST_INDEX_INT(OptionalOverload.OPTIONAL_LIST_INDEX_INT::newFunctionBinding),
720-
LIST_OPTINDEX_OPTIONAL_INT(OptionalOverload.LIST_OPTINDEX_OPTIONAL_INT::newFunctionBinding),
721-
OPTIONAL_LIST_OPTINDEX_OPTIONAL_INT(
722-
OptionalOverload.OPTIONAL_LIST_OPTINDEX_OPTIONAL_INT::newFunctionBinding);
723-
724-
private final FunctionBindingCreator bindingCreator;
725-
726-
@Override
727-
public CelFunctionBinding newFunctionBinding(
728-
CelOptions celOptions, RuntimeEquality runtimeEquality) {
729-
return bindingCreator.create(celOptions, runtimeEquality);
730-
}
731-
732-
OptionalValue(FunctionBindingCreator bindingCreator) {
733-
this.bindingCreator = bindingCreator;
734-
}
735-
}
736-
737700
private Overload() {}
738701
}
739702

runtime/src/main/java/dev/cel/runtime/standard/BUILD.bazel

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,36 +1226,6 @@ cel_android_library(
12261226
],
12271227
)
12281228

1229-
java_library(
1230-
name = "optional",
1231-
srcs = ["OptionalFunction.java"],
1232-
tags = [
1233-
],
1234-
deps = [
1235-
":standard_overload",
1236-
"//common:options",
1237-
"//runtime:function_binding",
1238-
"//runtime:runtime_equality",
1239-
"//runtime/standard:standard_function",
1240-
"@maven//:com_google_guava_guava",
1241-
],
1242-
)
1243-
1244-
cel_android_library(
1245-
name = "optional_android",
1246-
srcs = ["OptionalFunction.java"],
1247-
tags = [
1248-
],
1249-
deps = [
1250-
":standard_function_android",
1251-
":standard_overload_android",
1252-
"//common:options",
1253-
"//runtime:function_binding_android",
1254-
"//runtime:runtime_equality_android",
1255-
"@maven_android//:com_google_guava_guava",
1256-
],
1257-
)
1258-
12591229
java_library(
12601230
name = "size",
12611231
srcs = ["SizeFunction.java"],

0 commit comments

Comments
 (0)