Skip to content

Commit 14103e0

Browse files
l46kokcopybara-github
authored andcommitted
Add EvalOptionalIndex
PiperOrigin-RevId: 967370343
1 parent 426fa24 commit 14103e0

9 files changed

Lines changed: 441 additions & 12 deletions

File tree

extensions/src/test/java/dev/cel/extensions/CelOptionalLibraryTest.java

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ public void optionalOfNonZeroValue_withZeroValue_returnsEmptyOptionalValue(
280280
Object result = cel.createProgram(ast).eval();
281281

282282
assertThat(result).isInstanceOf(Optional.class);
283-
assertThat(result).isEqualTo(Optional.empty());
283+
assertThat((Optional<?>) result).isEmpty();
284284
}
285285

286286
@Test
@@ -306,7 +306,7 @@ public void optionalOfNonZeroValue_withNullValue_returnsEmptyOptionalValue() thr
306306
Object result = cel.createProgram(ast).eval();
307307

308308
assertThat(result).isInstanceOf(Optional.class);
309-
assertThat(result).isEqualTo(Optional.empty());
309+
assertThat((Optional<?>) result).isEmpty();
310310
}
311311

312312
@Test
@@ -317,7 +317,7 @@ public void optionalOfNonZeroValue_withEmptyMessage_returnsEmptyOptionalValue()
317317
Object result = cel.createProgram(ast).eval();
318318

319319
assertThat(result).isInstanceOf(Optional.class);
320-
assertThat(result).isEqualTo(Optional.empty());
320+
assertThat((Optional<?>) result).isEmpty();
321321
}
322322

323323
@Test
@@ -328,7 +328,7 @@ public void optionalNone_success() throws Exception {
328328
Object result = cel.createProgram(ast).eval();
329329

330330
assertThat(result).isInstanceOf(Optional.class);
331-
assertThat(result).isEqualTo(Optional.empty());
331+
assertThat((Optional<?>) result).isEmpty();
332332
}
333333

334334
@Test
@@ -592,7 +592,7 @@ public void optionalFieldSelection_onMap_returnsOptionalEmpty() throws Exception
592592

593593
Object result = cel.createProgram(ast).eval();
594594

595-
assertThat(result).isEqualTo(Optional.empty());
595+
assertThat((Optional<?>) result).isEmpty();
596596
}
597597

598598
@Test
@@ -882,7 +882,7 @@ public void optionalIndex_onOptionalMap_returnsOptionalEmpty() throws Exception
882882
cel.createProgram(ast)
883883
.eval(ImmutableMap.of("optm", Optional.of(ImmutableMap.of("c", ImmutableMap.of()))));
884884

885-
assertThat(result).isEqualTo(Optional.empty());
885+
assertThat((Optional<?>) result).isEmpty();
886886
}
887887

888888
@Test
@@ -900,7 +900,7 @@ public void optionalIndex_onMap_returnsOptionalEmpty() throws Exception {
900900
Object result =
901901
cel.createProgram(ast).eval(ImmutableMap.of("m", ImmutableMap.of("c", ImmutableMap.of())));
902902

903-
assertThat(result).isEqualTo(Optional.empty());
903+
assertThat((Optional<?>) result).isEmpty();
904904
}
905905

906906
@Test
@@ -982,7 +982,71 @@ public void optionalIndex_onList_returnsOptionalValue() throws Exception {
982982

983983
Object result = cel.createProgram(ast).eval(ImmutableMap.of("l", ImmutableList.of("hello")));
984984

985-
assertThat(result).isEqualTo(Optional.of("hello"));
985+
assertThat((Optional<?>) result).hasValue("hello");
986+
}
987+
988+
@Test
989+
public void optionalIndex_onList_negativeIndex_returnsOptionalEmpty() throws Exception {
990+
Cel cel =
991+
newCelBuilder()
992+
.addVar("l", ListType.create(SimpleType.STRING))
993+
.setResultType(OptionalType.create(SimpleType.STRING))
994+
.build();
995+
CelAbstractSyntaxTree ast = compile(cel, "l[?-1]");
996+
997+
Object result = cel.createProgram(ast).eval(ImmutableMap.of("l", ImmutableList.of("hello")));
998+
999+
assertThat((Optional<?>) result).isEmpty();
1000+
}
1001+
1002+
@Test
1003+
public void optionalIndex_onList_outOfBoundsIndex_returnsOptionalEmpty() throws Exception {
1004+
Cel cel =
1005+
newCelBuilder()
1006+
.addVar("l", ListType.create(SimpleType.STRING))
1007+
.setResultType(OptionalType.create(SimpleType.STRING))
1008+
.build();
1009+
CelAbstractSyntaxTree ast = compile(cel, "l[?5]");
1010+
1011+
Object result = cel.createProgram(ast).eval(ImmutableMap.of("l", ImmutableList.of("hello")));
1012+
1013+
assertThat((Optional<?>) result).isEmpty();
1014+
}
1015+
1016+
@Test
1017+
public void optionalIndex_targetIsUnknown_returnsUnknown() throws Exception {
1018+
Cel cel =
1019+
newCelBuilder()
1020+
.addVar("l", ListType.create(SimpleType.STRING))
1021+
.setResultType(OptionalType.create(SimpleType.STRING))
1022+
.build();
1023+
CelAbstractSyntaxTree ast = compile(cel, "l[?0]");
1024+
1025+
Object result =
1026+
cel.createProgram(ast)
1027+
.eval(PartialVars.of(CelAttributePattern.fromQualifiedIdentifier("l")));
1028+
1029+
assertThat(result).isInstanceOf(CelUnknownSet.class);
1030+
}
1031+
1032+
@Test
1033+
public void optionalIndex_indexIsUnknown_returnsUnknown() throws Exception {
1034+
Cel cel =
1035+
newCelBuilder()
1036+
.addVar("l", ListType.create(SimpleType.STRING))
1037+
.addVar("i", SimpleType.INT)
1038+
.setResultType(OptionalType.create(SimpleType.STRING))
1039+
.build();
1040+
CelAbstractSyntaxTree ast = compile(cel, "l[?i]");
1041+
1042+
Object result =
1043+
cel.createProgram(ast)
1044+
.eval(
1045+
PartialVars.of(
1046+
ImmutableMap.of("l", ImmutableList.of("hello")),
1047+
CelAttributePattern.fromQualifiedIdentifier("i")));
1048+
1049+
assertThat(result).isInstanceOf(CelUnknownSet.class);
9861050
}
9871051

9881052
@Test
@@ -1013,7 +1077,7 @@ public void optionalIndex_onOptionalList_returnsOptionalValue() throws Exception
10131077
cel.createProgram(ast)
10141078
.eval(ImmutableMap.of("optl", Optional.of(ImmutableList.of("hello"))));
10151079

1016-
assertThat(result).isEqualTo(Optional.of("hello"));
1080+
assertThat((Optional<?>) result).hasValue("hello");
10171081
}
10181082

10191083
@Test
@@ -1043,7 +1107,7 @@ public void traditionalIndex_onOptionalList_returnsOptionalEmpty() throws Except
10431107

10441108
Object result = cel.createProgram(ast).eval(ImmutableMap.of("optl", Optional.empty()));
10451109

1046-
assertThat(result).isEqualTo(Optional.empty());
1110+
assertThat((Optional<?>) result).isEmpty();
10471111
}
10481112

10491113
@Test

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ public static Qualifier fromGeneric(Object value) {
104104
return ofUint((UnsignedLong) value);
105105
} else if (value instanceof Long) {
106106
return ofInt((Long) value);
107+
} else if (value instanceof Integer) {
108+
return ofInt(((Integer) value).longValue());
107109
} else if (value instanceof Boolean) {
108110
return ofBool((boolean) value);
109111
} else if (value instanceof String) {

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

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ java_library(
2929
":eval_exhaustive_conditional",
3030
":eval_exhaustive_or",
3131
":eval_fold",
32+
":eval_index",
3233
":eval_late_bound_call",
34+
":eval_optional_index",
3335
":eval_optional_or",
3436
":eval_optional_or_value",
3537
":eval_optional_select_field",
@@ -227,6 +229,39 @@ java_library(
227229
"//runtime:evaluation_exception",
228230
"//runtime:interpretable",
229231
"//runtime:resolved_overload",
232+
"@maven//:com_google_errorprone_error_prone_annotations",
233+
],
234+
)
235+
236+
java_library(
237+
name = "eval_index",
238+
srcs = ["EvalIndex.java"],
239+
deps = [
240+
":eval_helpers",
241+
":planned_interpretable",
242+
"//common/ast",
243+
"//common/values",
244+
"//runtime:accumulated_unknowns",
245+
"//runtime:evaluation_exception",
246+
"//runtime:interpretable",
247+
"//runtime:resolved_overload",
248+
"@maven//:com_google_errorprone_error_prone_annotations",
249+
],
250+
)
251+
252+
java_library(
253+
name = "eval_optional_index",
254+
srcs = ["EvalOptionalIndex.java"],
255+
deps = [
256+
":eval_helpers",
257+
":planned_interpretable",
258+
"//common/ast",
259+
"//common/values",
260+
"//runtime:accumulated_unknowns",
261+
"//runtime:evaluation_exception",
262+
"//runtime:interpretable",
263+
"//runtime:resolved_overload",
264+
"@maven//:com_google_errorprone_error_prone_annotations",
230265
],
231266
)
232267

@@ -544,7 +579,9 @@ cel_android_library(
544579
":eval_exhaustive_conditional_android",
545580
":eval_exhaustive_or_android",
546581
":eval_fold_android",
582+
":eval_index_android",
547583
":eval_late_bound_call_android",
584+
":eval_optional_index_android",
548585
":eval_optional_or_android",
549586
":eval_optional_or_value_android",
550587
":eval_optional_select_field_android",
@@ -735,10 +772,43 @@ cel_android_library(
735772
":planned_interpretable_android",
736773
"//common/ast:ast_android",
737774
"//common/values:values_android",
775+
"//runtime:accumulated_unknowns_android",
776+
"//runtime:evaluation_exception",
777+
"//runtime:interpretable_android",
778+
"//runtime:resolved_overload_android",
779+
"@maven//:com_google_errorprone_error_prone_annotations",
780+
],
781+
)
782+
783+
cel_android_library(
784+
name = "eval_index_android",
785+
srcs = ["EvalIndex.java"],
786+
deps = [
787+
":eval_helpers_android",
788+
":planned_interpretable_android",
789+
"//common/ast:ast_android",
790+
"//common/values:values_android",
791+
"//runtime:accumulated_unknowns_android",
738792
"//runtime:evaluation_exception",
739793
"//runtime:interpretable_android",
740794
"//runtime:resolved_overload_android",
741-
"//runtime/src/main/java/dev/cel/runtime:accumulated_unknowns_android",
795+
"@maven//:com_google_errorprone_error_prone_annotations",
796+
],
797+
)
798+
799+
cel_android_library(
800+
name = "eval_optional_index_android",
801+
srcs = ["EvalOptionalIndex.java"],
802+
deps = [
803+
":eval_helpers_android",
804+
":planned_interpretable_android",
805+
"//common/ast:ast_android",
806+
"//common/values:values_android",
807+
"//runtime:accumulated_unknowns_android",
808+
"//runtime:evaluation_exception",
809+
"//runtime:interpretable_android",
810+
"//runtime:resolved_overload_android",
811+
"@maven//:com_google_errorprone_error_prone_annotations",
742812
],
743813
)
744814

runtime/src/main/java/dev/cel/runtime/planner/EvalBinary.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
import static dev.cel.runtime.planner.EvalHelpers.evalNonstrictly;
1818
import static dev.cel.runtime.planner.EvalHelpers.evalStrictly;
1919

20+
import com.google.errorprone.annotations.Immutable;
2021
import dev.cel.common.ast.CelExpr;
2122
import dev.cel.common.values.CelValueConverter;
2223
import dev.cel.runtime.AccumulatedUnknowns;
2324
import dev.cel.runtime.CelEvaluationException;
2425
import dev.cel.runtime.CelResolvedOverload;
2526
import dev.cel.runtime.GlobalResolver;
2627

28+
@Immutable
2729
final class EvalBinary extends PlannedInterpretable {
2830

2931
private final String functionName;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2026 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.planner;
16+
17+
import static dev.cel.runtime.planner.EvalHelpers.evalNonstrictly;
18+
import static dev.cel.runtime.planner.EvalHelpers.evalStrictly;
19+
20+
import com.google.errorprone.annotations.Immutable;
21+
import dev.cel.common.ast.CelExpr;
22+
import dev.cel.common.values.CelValueConverter;
23+
import dev.cel.runtime.AccumulatedUnknowns;
24+
import dev.cel.runtime.CelEvaluationException;
25+
import dev.cel.runtime.CelResolvedOverload;
26+
import dev.cel.runtime.GlobalResolver;
27+
28+
@Immutable
29+
final class EvalIndex extends PlannedInterpretable {
30+
31+
private final String functionName;
32+
private final CelResolvedOverload resolvedOverload;
33+
private final PlannedInterpretable target;
34+
private final PlannedInterpretable index;
35+
private final CelValueConverter celValueConverter;
36+
37+
@Override
38+
Object evalInternal(GlobalResolver resolver, ExecutionFrame frame) throws CelEvaluationException {
39+
boolean isStrict = resolvedOverload.isStrict();
40+
Object targetVal =
41+
isStrict ? evalStrictly(target, resolver, frame) : evalNonstrictly(target, resolver, frame);
42+
Object indexVal =
43+
isStrict ? evalStrictly(index, resolver, frame) : evalNonstrictly(index, resolver, frame);
44+
45+
if (isStrict) {
46+
AccumulatedUnknowns unknowns = AccumulatedUnknowns.maybeMerge(null, targetVal);
47+
unknowns = AccumulatedUnknowns.maybeMerge(unknowns, indexVal);
48+
if (unknowns != null) {
49+
return unknowns;
50+
}
51+
}
52+
53+
return EvalHelpers.dispatch(
54+
functionName, resolvedOverload, celValueConverter, targetVal, indexVal);
55+
}
56+
57+
static EvalIndex create(
58+
CelExpr expr,
59+
String functionName,
60+
CelResolvedOverload resolvedOverload,
61+
PlannedInterpretable target,
62+
PlannedInterpretable index,
63+
CelValueConverter celValueConverter) {
64+
return new EvalIndex(expr, functionName, resolvedOverload, target, index, celValueConverter);
65+
}
66+
67+
private EvalIndex(
68+
CelExpr expr,
69+
String functionName,
70+
CelResolvedOverload resolvedOverload,
71+
PlannedInterpretable target,
72+
PlannedInterpretable index,
73+
CelValueConverter celValueConverter) {
74+
super(expr);
75+
this.functionName = functionName;
76+
this.resolvedOverload = resolvedOverload;
77+
this.target = target;
78+
this.index = index;
79+
this.celValueConverter = celValueConverter;
80+
}
81+
}

0 commit comments

Comments
 (0)