Skip to content

Commit 4f6a571

Browse files
l46kokcopybara-github
authored andcommitted
Support partial evaluation via unknowns in planner
PiperOrigin-RevId: 885217146
1 parent b3663bf commit 4f6a571

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1665
-321
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ java_library(
3838
"//runtime:interpreter_util",
3939
"//runtime:lite_runtime",
4040
"//runtime:lite_runtime_factory",
41+
"//runtime:partial_vars",
42+
"//runtime:unknown_attributes",
4143
"@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto",
4244
"@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto",
4345
"@cel_spec//proto/cel/expr/conformance/test:simple_java_proto",

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

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@
4949
import dev.cel.expr.conformance.proto3.TestAllTypes.NestedMessage;
5050
import dev.cel.parser.CelMacro;
5151
import dev.cel.parser.CelStandardMacro;
52+
import dev.cel.runtime.CelAttributePattern;
5253
import dev.cel.runtime.CelEvaluationException;
5354
import dev.cel.runtime.CelFunctionBinding;
5455
import dev.cel.runtime.CelRuntime;
5556
import dev.cel.runtime.InterpreterUtil;
57+
import dev.cel.runtime.PartialVars;
5658
import java.time.Duration;
5759
import java.time.Instant;
5860
import java.util.List;
@@ -897,14 +899,12 @@ public void optionalIndex_onMap_returnsOptionalValue() throws Exception {
897899
@TestParameters("{source: '{?x: x}'}")
898900
public void optionalIndex_onMapWithUnknownInput_returnsUnknownResult(String source)
899901
throws Exception {
900-
if (testMode.equals(TestMode.PLANNER_CHECKED) || testMode.equals(TestMode.PLANNER_PARSE_ONLY)) {
901-
// TODO: Uncomment once unknowns is implemented
902-
return;
903-
}
904902
Cel cel = newCelBuilder().addVar("x", OptionalType.create(SimpleType.INT)).build();
905903
CelAbstractSyntaxTree ast = compile(cel, source);
906904

907-
Object result = cel.createProgram(ast).eval();
905+
Object result =
906+
cel.createProgram(ast)
907+
.eval(PartialVars.of(CelAttributePattern.fromQualifiedIdentifier("x")));
908908

909909
assertThat(InterpreterUtil.isUnknown(result)).isTrue();
910910
}
@@ -987,18 +987,16 @@ public void optionalIndex_onOptionalList_returnsOptionalValue() throws Exception
987987

988988
@Test
989989
public void optionalIndex_onListWithUnknownInput_returnsUnknownResult() throws Exception {
990-
if (testMode.equals(TestMode.PLANNER_CHECKED) || testMode.equals(TestMode.PLANNER_PARSE_ONLY)) {
991-
// TODO: Uncomment once unknowns is implemented
992-
return;
993-
}
994990
Cel cel =
995991
newCelBuilder()
996992
.addVar("x", OptionalType.create(SimpleType.INT))
997993
.setResultType(ListType.create(SimpleType.INT))
998994
.build();
999995
CelAbstractSyntaxTree ast = compile(cel, "[?x]");
1000996

1001-
Object result = cel.createProgram(ast).eval();
997+
Object result =
998+
cel.createProgram(ast)
999+
.eval(PartialVars.of(CelAttributePattern.fromQualifiedIdentifier("x")));
10021000

10031001
assertThat(InterpreterUtil.isUnknown(result)).isTrue();
10041002
}
@@ -1017,6 +1015,29 @@ public void traditionalIndex_onOptionalList_returnsOptionalEmpty() throws Except
10171015
assertThat(result).isEqualTo(Optional.empty());
10181016
}
10191017

1018+
@Test
1019+
public void optionalFieldSelect_fieldMarkedUnknown_returnsUnknownSet() throws Exception {
1020+
if (testMode.equals(TestMode.LEGACY_CHECKED)) {
1021+
// This case is not possible to setup for legacy runtime
1022+
return;
1023+
}
1024+
1025+
Cel cel =
1026+
newCelBuilder()
1027+
.addVar("msg", StructTypeReference.create(TestAllTypes.getDescriptor().getFullName()))
1028+
.build();
1029+
CelAbstractSyntaxTree ast = compile(cel, "msg.?single_int32");
1030+
1031+
Object result =
1032+
cel.createProgram(ast)
1033+
.eval(
1034+
PartialVars.of(
1035+
ImmutableMap.of("msg", TestAllTypes.newBuilder().setSingleInt32(42).build()),
1036+
CelAttributePattern.fromQualifiedIdentifier("msg.single_int32")));
1037+
1038+
assertThat(InterpreterUtil.isUnknown(result)).isTrue();
1039+
}
1040+
10201041
@Test
10211042
// LHS
10221043
@TestParameters("{expression: 'optx.or(optional.of(1))'}")
@@ -1026,18 +1047,16 @@ public void traditionalIndex_onOptionalList_returnsOptionalEmpty() throws Except
10261047
@TestParameters("{expression: 'optional.none().orValue(optx)'}")
10271048
public void optionalChainedFunctions_lhsIsUnknown_returnsUnknown(String expression)
10281049
throws Exception {
1029-
if (testMode.equals(TestMode.PLANNER_CHECKED) || testMode.equals(TestMode.PLANNER_PARSE_ONLY)) {
1030-
// TODO: Uncomment once unknowns is implemented
1031-
return;
1032-
}
10331050
Cel cel =
10341051
newCelBuilder()
10351052
.addVar("optx", OptionalType.create(SimpleType.INT))
10361053
.addVar("x", SimpleType.INT)
10371054
.build();
10381055
CelAbstractSyntaxTree ast = compile(cel, expression);
10391056

1040-
Object result = cel.createProgram(ast).eval();
1057+
Object result =
1058+
cel.createProgram(ast)
1059+
.eval(PartialVars.of(CelAttributePattern.fromQualifiedIdentifier("optx")));
10411060

10421061
assertThat(InterpreterUtil.isUnknown(result)).isTrue();
10431062
}

runtime/BUILD.bazel

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,17 @@ java_library(
351351
"//runtime/src/main/java/dev/cel/runtime:runtime_planner_impl",
352352
],
353353
)
354+
355+
java_library(
356+
name = "accumulated_unknowns",
357+
visibility = ["//:internal"],
358+
exports = [
359+
"//runtime/src/main/java/dev/cel/runtime:accumulated_unknowns",
360+
],
361+
)
362+
363+
java_library(
364+
name = "partial_vars",
365+
visibility = ["//:internal"],
366+
exports = ["//runtime/src/main/java/dev/cel/runtime:partial_vars"],
367+
)

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

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,23 @@
1515
package dev.cel.runtime;
1616

1717
import com.google.errorprone.annotations.CanIgnoreReturnValue;
18+
import dev.cel.common.annotations.Internal;
1819
import java.util.ArrayList;
1920
import java.util.Arrays;
2021
import java.util.Collection;
2122
import java.util.HashSet;
2223
import java.util.Set;
24+
import org.jspecify.annotations.Nullable;
2325

2426
/**
2527
* An internal representation used for fast accumulation of unknown expr IDs and attributes. For
2628
* safety, this object should never be returned as an evaluated result and instead be adapted into
2729
* an immutable CelUnknownSet.
30+
*
31+
* <p>CEL Library Internals. Do Not Use.
2832
*/
29-
final class AccumulatedUnknowns {
33+
@Internal
34+
public final class AccumulatedUnknowns {
3035
private static final int MAX_UNKNOWN_ATTRIBUTE_SIZE = 500_000;
3136
private final Set<Long> exprIds;
3237
private final Set<CelAttribute> attributes;
@@ -39,8 +44,21 @@ Set<CelAttribute> attributes() {
3944
return attributes;
4045
}
4146

47+
/**
48+
* Evaluates if the right hand side is an accumulated unknown, and if so, merges it into the
49+
* accumulator.
50+
*/
51+
public static @Nullable AccumulatedUnknowns maybeMerge(
52+
@Nullable AccumulatedUnknowns accumulator, Object newValue) {
53+
if (newValue instanceof AccumulatedUnknowns) {
54+
AccumulatedUnknowns newUnknowns = (AccumulatedUnknowns) newValue;
55+
return accumulator == null ? newUnknowns : accumulator.merge(newUnknowns);
56+
}
57+
return accumulator;
58+
}
59+
4260
@CanIgnoreReturnValue
43-
AccumulatedUnknowns merge(AccumulatedUnknowns arg) {
61+
public AccumulatedUnknowns merge(AccumulatedUnknowns arg) {
4462
enforceMaxAttributeSize(this.attributes, arg.attributes);
4563
this.exprIds.addAll(arg.exprIds);
4664
this.attributes.addAll(arg.attributes);
@@ -55,7 +73,8 @@ static AccumulatedUnknowns create(Collection<Long> ids) {
5573
return create(ids, new ArrayList<>());
5674
}
5775

58-
static AccumulatedUnknowns create(Collection<Long> exprIds, Collection<CelAttribute> attributes) {
76+
public static AccumulatedUnknowns create(
77+
Collection<Long> exprIds, Collection<CelAttribute> attributes) {
5978
return new AccumulatedUnknowns(new HashSet<>(exprIds), new HashSet<>(attributes));
6079
}
6180

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

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,7 @@ java_library(
826826
":evaluation_listener",
827827
":function_binding",
828828
":function_resolver",
829+
":partial_vars",
829830
":program",
830831
":proto_message_runtime_equality",
831832
":runtime",
@@ -938,6 +939,7 @@ java_library(
938939
":function_resolver",
939940
":interpretable",
940941
":interpreter",
942+
":partial_vars",
941943
":program",
942944
":proto_message_activation_factory",
943945
":runtime_equality",
@@ -955,7 +957,6 @@ java_library(
955957
"@maven//:com_google_errorprone_error_prone_annotations",
956958
"@maven//:com_google_guava_guava",
957959
"@maven//:com_google_protobuf_protobuf_java",
958-
"@maven//:org_jspecify_jspecify",
959960
],
960961
)
961962

@@ -1014,6 +1015,7 @@ java_library(
10141015
":evaluation_exception",
10151016
":function_resolver",
10161017
":interpretable",
1018+
":partial_vars",
10171019
":program",
10181020
":variable_resolver",
10191021
"//:auto_value",
@@ -1029,6 +1031,7 @@ cel_android_library(
10291031
":evaluation_exception",
10301032
":function_resolver_android",
10311033
":interpretable_android",
1034+
":partial_vars_android",
10321035
":program_android",
10331036
":variable_resolver",
10341037
"//:auto_value",
@@ -1199,6 +1202,7 @@ java_library(
11991202
":unknown_attributes",
12001203
"//common/annotations",
12011204
"@maven//:com_google_errorprone_error_prone_annotations",
1205+
"@maven//:com_google_guava_guava",
12021206
"@maven//:org_jspecify_jspecify",
12031207
],
12041208
)
@@ -1214,6 +1218,7 @@ cel_android_library(
12141218
"//common/annotations",
12151219
"@maven//:com_google_errorprone_error_prone_annotations",
12161220
"@maven//:org_jspecify_jspecify",
1221+
"@maven_android//:com_google_guava_guava",
12171222
],
12181223
)
12191224

@@ -1273,10 +1278,13 @@ java_library(
12731278
java_library(
12741279
name = "accumulated_unknowns",
12751280
srcs = ["AccumulatedUnknowns.java"],
1276-
visibility = ["//visibility:private"],
1281+
tags = [
1282+
],
12771283
deps = [
12781284
":unknown_attributes",
1285+
"//common/annotations",
12791286
"@maven//:com_google_errorprone_error_prone_annotations",
1287+
"@maven//:org_jspecify_jspecify",
12801288
],
12811289
)
12821290

@@ -1286,7 +1294,9 @@ cel_android_library(
12861294
visibility = ["//visibility:private"],
12871295
deps = [
12881296
":unknown_attributes_android",
1297+
"//common/annotations",
12891298
"@maven//:com_google_errorprone_error_prone_annotations",
1299+
"@maven//:org_jspecify_jspecify",
12901300
],
12911301
)
12921302

@@ -1318,6 +1328,34 @@ cel_android_library(
13181328
],
13191329
)
13201330

1331+
java_library(
1332+
name = "partial_vars",
1333+
srcs = ["PartialVars.java"],
1334+
tags = [
1335+
],
1336+
deps = [
1337+
":variable_resolver",
1338+
"//:auto_value",
1339+
"//runtime:unknown_attributes",
1340+
"@maven//:com_google_errorprone_error_prone_annotations",
1341+
"@maven//:com_google_guava_guava",
1342+
],
1343+
)
1344+
1345+
cel_android_library(
1346+
name = "partial_vars_android",
1347+
srcs = ["PartialVars.java"],
1348+
tags = [
1349+
],
1350+
deps = [
1351+
":variable_resolver",
1352+
"//:auto_value",
1353+
"//runtime:unknown_attributes_android",
1354+
"@maven//:com_google_errorprone_error_prone_annotations",
1355+
"@maven_android//:com_google_guava_guava",
1356+
],
1357+
)
1358+
13211359
java_library(
13221360
name = "program",
13231361
srcs = ["Program.java"],
@@ -1326,6 +1364,7 @@ java_library(
13261364
deps = [
13271365
":evaluation_exception",
13281366
":function_resolver",
1367+
":partial_vars",
13291368
":variable_resolver",
13301369
"@maven//:com_google_errorprone_error_prone_annotations",
13311370
],
@@ -1339,8 +1378,8 @@ cel_android_library(
13391378
deps = [
13401379
":evaluation_exception",
13411380
":function_resolver_android",
1381+
":partial_vars_android",
13421382
":variable_resolver",
1343-
"//:auto_value",
13441383
"@maven//:com_google_errorprone_error_prone_annotations",
13451384
],
13461385
)

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ public Object eval(
134134
return program.eval(resolver, lateBoundFunctionResolver);
135135
}
136136

137+
@Override
138+
public Object eval(PartialVars partialVars) throws CelEvaluationException {
139+
return program.eval(partialVars);
140+
}
141+
137142
@Override
138143
public Object trace(CelEvaluationListener listener) throws CelEvaluationException {
139144
throw new UnsupportedOperationException("Trace is not yet supported.");

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ static CelUnknownSet create(Iterable<Long> unknownExprIds) {
5959
return create(ImmutableSet.of(), ImmutableSet.copyOf(unknownExprIds));
6060
}
6161

62-
static CelUnknownSet create(
62+
public static CelUnknownSet create(
6363
ImmutableSet<CelAttribute> attributes, ImmutableSet<Long> unknownExprIds) {
6464
return new AutoValue_CelUnknownSet(attributes, unknownExprIds);
6565
}

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package dev.cel.runtime;
1616

17+
import com.google.common.collect.ImmutableSet;
1718
import com.google.errorprone.annotations.CheckReturnValue;
1819
import dev.cel.common.annotations.Internal;
1920
import org.jspecify.annotations.Nullable;
@@ -55,23 +56,33 @@ public static boolean isUnknown(Object obj) {
5556
return obj instanceof CelUnknownSet;
5657
}
5758

58-
static boolean isAccumulatedUnknowns(Object obj) {
59+
public static boolean isAccumulatedUnknowns(Object obj) {
5960
return obj instanceof AccumulatedUnknowns;
6061
}
6162

6263
/** If the argument is {@link CelUnknownSet}, adapts it into {@link AccumulatedUnknowns} */
63-
static Object maybeAdaptToAccumulatedUnknowns(Object val) {
64+
public static Object maybeAdaptToAccumulatedUnknowns(Object val) {
6465
if (!(val instanceof CelUnknownSet)) {
6566
return val;
6667
}
6768

6869
return adaptToAccumulatedUnknowns((CelUnknownSet) val);
6970
}
7071

71-
static AccumulatedUnknowns adaptToAccumulatedUnknowns(CelUnknownSet unknowns) {
72+
public static AccumulatedUnknowns adaptToAccumulatedUnknowns(CelUnknownSet unknowns) {
7273
return AccumulatedUnknowns.create(unknowns.unknownExprIds(), unknowns.attributes());
7374
}
7475

76+
public static Object maybeAdaptToCelUnknownSet(Object val) {
77+
if (!(val instanceof AccumulatedUnknowns)) {
78+
return val;
79+
}
80+
81+
AccumulatedUnknowns unknowns = (AccumulatedUnknowns) val;
82+
return CelUnknownSet.create(
83+
ImmutableSet.copyOf(unknowns.attributes()), ImmutableSet.copyOf(unknowns.exprIds()));
84+
}
85+
7586
/**
7687
* Enforces strictness on both lhs/rhs arguments from logical operators (i.e: intentionally throws
7788
* an appropriate exception when {@link Throwable} is encountered as part of evaluated result.

0 commit comments

Comments
 (0)