Skip to content

Commit e1b6c11

Browse files
l46kokcopybara-github
authored andcommitted
Evaluate CEL's null and bytes to their native equivalent types
PiperOrigin-RevId: 796545163
1 parent 5b06cc0 commit e1b6c11

File tree

77 files changed

+727
-298
lines changed

Some content is hidden

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

77 files changed

+727
-298
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ java_library(
4141
"//common/types:cel_proto_types",
4242
"//common/types:message_type_provider",
4343
"//common/types:type_providers",
44+
"//common/values",
45+
"//common/values:cel_byte_string",
4446
"//compiler",
4547
"//compiler:compiler_builder",
4648
"//extensions",
@@ -64,7 +66,6 @@ java_library(
6466
"@maven//:com_google_truth_extensions_truth_proto_extension",
6567
"@maven//:junit_junit",
6668
"@maven//:org_jspecify_jspecify",
67-
"@maven_android//:com_google_protobuf_protobuf_javalite",
6869
],
6970
)
7071

bundle/src/test/java/dev/cel/bundle/CelImplTest.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
import com.google.protobuf.Empty;
4949
import com.google.protobuf.FieldMask;
5050
import com.google.protobuf.Message;
51-
import com.google.protobuf.NullValue;
5251
import com.google.protobuf.Struct;
5352
import com.google.protobuf.TextFormat;
5453
import com.google.protobuf.Timestamp;
@@ -86,6 +85,8 @@
8685
import dev.cel.common.types.ProtoMessageTypeProvider;
8786
import dev.cel.common.types.SimpleType;
8887
import dev.cel.common.types.StructTypeReference;
88+
import dev.cel.common.values.CelByteString;
89+
import dev.cel.common.values.NullValue;
8990
import dev.cel.compiler.CelCompiler;
9091
import dev.cel.compiler.CelCompilerFactory;
9192
import dev.cel.compiler.CelCompilerImpl;
@@ -108,6 +109,7 @@
108109
import dev.cel.runtime.UnknownContext;
109110
import dev.cel.testing.testdata.proto3.StandaloneGlobalEnum;
110111
import java.util.ArrayList;
112+
import java.util.LinkedHashMap;
111113
import java.util.List;
112114
import java.util.Map;
113115
import java.util.Optional;
@@ -1019,7 +1021,10 @@ public void program_enumTypeReferenceResolution(boolean resolveTypeDependencies)
10191021
Cel cel =
10201022
standardCelBuilderWithMacros()
10211023
.setOptions(
1022-
CelOptions.current().resolveTypeDependencies(resolveTypeDependencies).build())
1024+
CelOptions.current()
1025+
.evaluateCanonicalTypesToNativeValues(true)
1026+
.resolveTypeDependencies(resolveTypeDependencies)
1027+
.build())
10231028
.addMessageTypes(Struct.getDescriptor())
10241029
.setResultType(StructTypeReference.create("google.protobuf.NullValue"))
10251030
.setContainer(CelContainer.ofName("google.protobuf"))
@@ -1037,7 +1042,11 @@ public void program_enumTypeReferenceResolution(boolean resolveTypeDependencies)
10371042
public void program_enumTypeTransitiveResolution() throws Exception {
10381043
Cel cel =
10391044
standardCelBuilderWithMacros()
1040-
.setOptions(CelOptions.current().resolveTypeDependencies(true).build())
1045+
.setOptions(
1046+
CelOptions.current()
1047+
.evaluateCanonicalTypesToNativeValues(true)
1048+
.resolveTypeDependencies(true)
1049+
.build())
10411050
.addMessageTypes(Proto2ExtensionScopedMessage.getDescriptor())
10421051
.setResultType(StructTypeReference.create("google.protobuf.NullValue"))
10431052
.setContainer(CelContainer.ofName("google.protobuf"))
@@ -1626,7 +1635,11 @@ public void programAdvanceEvaluation_indexOnUnknownContainer() throws Exception
16261635
public void programAdvanceEvaluation_unsupportedIndexIgnored() throws Exception {
16271636
Cel cel =
16281637
standardCelBuilderWithMacros()
1629-
.setOptions(CelOptions.current().enableUnknownTracking(true).build())
1638+
.setOptions(
1639+
CelOptions.current()
1640+
.evaluateCanonicalTypesToNativeValues(true)
1641+
.enableUnknownTracking(true)
1642+
.build())
16301643
.addVar("unk", MapType.create(SimpleType.STRING, SimpleType.BOOL))
16311644
.setContainer(CelContainer.ofName(""))
16321645
.addFunctionBindings()
@@ -1654,7 +1667,7 @@ public void programAdvanceEvaluation_unsupportedIndexIgnored() throws Exception
16541667
UnknownContext.create(
16551668
fromMap(
16561669
ImmutableMap.of(
1657-
"unk", ImmutableMap.of(ByteString.copyFromUtf8("a"), false))),
1670+
"unk", ImmutableMap.of(CelByteString.copyFromUtf8("a"), false))),
16581671
ImmutableList.of())))
16591672
.isEqualTo(false);
16601673
}
@@ -2077,6 +2090,23 @@ public void program_regexProgramSizeExceedsLimit_throws() throws Exception {
20772090
assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.INVALID_ARGUMENT);
20782091
}
20792092

2093+
@Test
2094+
@SuppressWarnings("unchecked") // test only
2095+
public void program_evaluateCanonicalTypesToNativeTypesDisabled_producesProtoValues()
2096+
throws Exception {
2097+
Cel cel =
2098+
standardCelBuilderWithMacros()
2099+
.setOptions(CelOptions.current().evaluateCanonicalTypesToNativeValues(false).build())
2100+
.build();
2101+
CelAbstractSyntaxTree ast = cel.compile("[null, {b'abc': null}]").getAst();
2102+
Map<ByteString, Object> expectedNestedMap = new LinkedHashMap<>();
2103+
expectedNestedMap.put(ByteString.copyFromUtf8("abc"), com.google.protobuf.NullValue.NULL_VALUE);
2104+
2105+
List<Object> result = (List<Object>) cel.createProgram(ast).eval();
2106+
2107+
assertThat(result).containsExactly(com.google.protobuf.NullValue.NULL_VALUE, expectedNestedMap);
2108+
}
2109+
20802110
@Test
20812111
public void toBuilder_isImmutable() {
20822112
CelBuilder celBuilder = CelFactory.standardCelBuilder();

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ java_library(
206206
],
207207
deps = [
208208
"//common/internal:proto_time_utils",
209+
"//common/values",
210+
"//common/values:cel_byte_string",
209211
"@maven//:com_google_errorprone_error_prone_annotations",
210212
"@maven//:com_google_guava_guava",
211213
"@maven//:com_google_protobuf_protobuf_java",

common/src/main/java/dev/cel/common/CelOptions.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ public enum ProtoUnsetFieldOptions {
109109

110110
public abstract int comprehensionMaxIterations();
111111

112+
public abstract boolean evaluateCanonicalTypesToNativeValues();
113+
112114
public abstract boolean unwrapWellKnownTypesOnFunctionDispatch();
113115

114116
public abstract ProtoUnsetFieldOptions fromProtoUnsetFieldOption();
@@ -150,6 +152,7 @@ public static Builder newBuilder() {
150152
.enableNamespacedDeclarations(true)
151153
// Evaluation options
152154
.disableCelStandardEquality(true)
155+
.evaluateCanonicalTypesToNativeValues(false)
153156
.enableShortCircuiting(true)
154157
.enableRegexPartialMatch(false)
155158
.enableUnsignedComparisonAndArithmeticIsUnsigned(false)
@@ -450,6 +453,19 @@ public abstract static class Builder {
450453
*/
451454
public abstract Builder comprehensionMaxIterations(int value);
452455

456+
/**
457+
* If set, canonical CEL types such as bytes and CEL null will return their native value
458+
* equivalents instead of protobuf based values. Specifically:
459+
*
460+
* <ul>
461+
* <li>Bytes: {@code dev.cel.common.values.CelByteString} instead of {@code
462+
* com.google.protobuf.ByteString}.
463+
* <li>CEL null: {@code dev.cel.common.values.NullValue} instead of {@code
464+
* com.google.protobuf.NullValue}.
465+
* </ul>
466+
*/
467+
public abstract Builder evaluateCanonicalTypesToNativeValues(boolean value);
468+
453469
/**
454470
* If disabled, CEL runtime will no longer adapt the function dispatch results for protobuf's
455471
* well known types to other types. This option is enabled by default.

common/src/main/java/dev/cel/common/CelProtoJsonAdapter.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import com.google.common.base.Joiner;
1919
import com.google.common.primitives.UnsignedLong;
2020
import com.google.errorprone.annotations.Immutable;
21-
import com.google.protobuf.ByteString;
2221
import com.google.protobuf.Duration;
2322
import com.google.protobuf.Empty;
2423
import com.google.protobuf.FieldMask;
@@ -28,6 +27,7 @@
2827
import com.google.protobuf.Timestamp;
2928
import com.google.protobuf.Value;
3029
import dev.cel.common.internal.ProtoTimeUtils;
30+
import dev.cel.common.values.CelByteString;
3131
import java.util.ArrayList;
3232
import java.util.Base64;
3333
import java.util.List;
@@ -71,7 +71,7 @@ public static <K extends String, V> Struct adaptToJsonStructValue(Map<K, V> map)
7171
@SuppressWarnings("unchecked")
7272
public static Value adaptValueToJsonValue(Object value) {
7373
Value.Builder json = Value.newBuilder();
74-
if (value == null || value instanceof NullValue) {
74+
if (value == null || value instanceof dev.cel.common.values.NullValue) {
7575
return json.setNullValue(NullValue.NULL_VALUE).build();
7676
}
7777
if (value instanceof Boolean) {
@@ -93,9 +93,9 @@ public static Value adaptValueToJsonValue(Object value) {
9393
if (value instanceof Float || value instanceof Double) {
9494
return json.setNumberValue(((Number) value).doubleValue()).build();
9595
}
96-
if (value instanceof ByteString) {
96+
if (value instanceof CelByteString) {
9797
return json.setStringValue(
98-
Base64.getEncoder().encodeToString(((ByteString) value).toByteArray()))
98+
Base64.getEncoder().encodeToString(((CelByteString) value).toByteArray()))
9999
.build();
100100
}
101101
if (value instanceof String) {

common/src/main/java/dev/cel/common/ast/BUILD.bazel

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ java_library(
4949
deps = [
5050
"//:auto_value",
5151
"//common/annotations",
52+
"//common/values",
53+
"//common/values:cel_byte_string",
5254
"@maven//:com_google_errorprone_error_prone_annotations",
5355
"@maven//:com_google_guava_guava",
5456
"@maven//:com_google_protobuf_protobuf_java",
@@ -62,9 +64,12 @@ java_library(
6264
],
6365
deps = [
6466
":ast",
67+
"//common/values",
68+
"//common/values:cel_byte_string",
6569
"@cel_spec//proto/cel/expr:checked_java_proto",
6670
"@cel_spec//proto/cel/expr:syntax_java_proto",
6771
"@maven//:com_google_guava_guava",
72+
"@maven//:com_google_protobuf_protobuf_java",
6873
],
6974
)
7075

@@ -75,9 +80,12 @@ cel_android_library(
7580
],
7681
deps = [
7782
":ast_android",
83+
"//common/values:cel_byte_string",
84+
"//common/values:values_android",
7885
"@cel_spec//proto/cel/expr:checked_java_proto_lite",
7986
"@cel_spec//proto/cel/expr:syntax_java_proto_lite",
8087
"@maven_android//:com_google_guava_guava",
88+
"@maven_android//:com_google_protobuf_protobuf_javalite",
8189
],
8290
)
8391

@@ -88,8 +96,11 @@ java_library(
8896
],
8997
deps = [
9098
":ast",
99+
"//common/values",
100+
"//common/values:cel_byte_string",
91101
"@com_google_googleapis//google/api/expr/v1alpha1:expr_java_proto",
92102
"@maven//:com_google_guava_guava",
103+
"@maven//:com_google_protobuf_protobuf_java",
93104
],
94105
)
95106

@@ -112,8 +123,8 @@ java_library(
112123
deps = [
113124
":ast",
114125
"//common/annotations",
126+
"//common/values:cel_byte_string",
115127
"@maven//:com_google_guava_guava",
116-
"@maven//:com_google_protobuf_protobuf_java",
117128
],
118129
)
119130

@@ -136,6 +147,8 @@ cel_android_library(
136147
deps = [
137148
"//:auto_value",
138149
"//common/annotations",
150+
"//common/values:cel_byte_string",
151+
"//common/values:values_android",
139152
"@maven//:com_google_errorprone_error_prone_annotations",
140153
"@maven_android//:com_google_guava_guava",
141154
"@maven_android//:com_google_protobuf_protobuf_javalite",

common/src/main/java/dev/cel/common/ast/CelConstant.java

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
import com.google.common.collect.ImmutableSet;
2020
import com.google.common.primitives.UnsignedLong;
2121
import com.google.errorprone.annotations.Immutable;
22+
import com.google.errorprone.annotations.InlineMe;
2223
import com.google.protobuf.ByteString;
2324
import com.google.protobuf.Duration;
24-
import com.google.protobuf.NullValue;
2525
import com.google.protobuf.Timestamp;
2626
import dev.cel.common.annotations.Internal;
27+
import dev.cel.common.values.CelByteString;
28+
import dev.cel.common.values.NullValue;
2729

2830
/**
2931
* Represents a primitive literal.
@@ -42,7 +44,7 @@ public abstract class CelConstant {
4244
UnsignedLong.class,
4345
Double.class,
4446
String.class,
45-
ByteString.class);
47+
CelByteString.class);
4648

4749
/** Represents the type of the Constant */
4850
public enum Kind {
@@ -92,7 +94,7 @@ public abstract static class CelConstantNotSet {}
9294

9395
public abstract String stringValue();
9496

95-
public abstract ByteString bytesValue();
97+
public abstract CelByteString bytesValue();
9698

9799
/**
98100
* @deprecated Do not use. Timestamp is no longer built-in CEL type.
@@ -134,10 +136,46 @@ public static CelConstant ofValue(String value) {
134136
return AutoOneOf_CelConstant.stringValue(value);
135137
}
136138

137-
public static CelConstant ofValue(ByteString value) {
139+
public static CelConstant ofValue(CelByteString value) {
138140
return AutoOneOf_CelConstant.bytesValue(value);
139141
}
140142

143+
/**
144+
* @deprecated Use native type equivalent {@link #ofValue(NullValue)} instead.
145+
*/
146+
@InlineMe(
147+
replacement = "CelConstant.ofValue(NullValue.NULL_VALUE)",
148+
imports = {"dev.cel.common.ast.CelConstant", "dev.cel.common.values.NullValue"})
149+
@Deprecated
150+
public static CelConstant ofValue(com.google.protobuf.NullValue unused) {
151+
return ofValue(NullValue.NULL_VALUE);
152+
}
153+
154+
/**
155+
* @deprecated Use native type equivalent {@link #ofValue(CelByteString)} instead.
156+
*/
157+
@Deprecated
158+
public static CelConstant ofValue(ByteString value) {
159+
CelByteString celByteString = CelByteString.of(value.toByteArray());
160+
return ofValue(celByteString);
161+
}
162+
163+
/**
164+
* @deprecated Do not use. Duration is no longer built-in CEL type.
165+
*/
166+
@Deprecated
167+
public static CelConstant ofValue(Duration value) {
168+
return AutoOneOf_CelConstant.durationValue(value);
169+
}
170+
171+
/**
172+
* @deprecated Do not use. Timestamp is no longer built-in CEL type.
173+
*/
174+
@Deprecated
175+
public static CelConstant ofValue(Timestamp value) {
176+
return AutoOneOf_CelConstant.timestampValue(value);
177+
}
178+
141179
/** Checks whether the provided Java object is a valid CelConstant value. */
142180
public static boolean isConstantValue(Object value) {
143181
return CONSTANT_CLASSES.contains(value.getClass());
@@ -163,26 +201,10 @@ public static CelConstant ofObjectValue(Object value) {
163201
return ofValue((double) value);
164202
} else if (value instanceof String) {
165203
return ofValue((String) value);
166-
} else if (value instanceof ByteString) {
167-
return ofValue((ByteString) value);
204+
} else if (value instanceof CelByteString) {
205+
return ofValue((CelByteString) value);
168206
}
169207

170208
throw new IllegalArgumentException("Value is not a CelConstant: " + value);
171209
}
172-
173-
/**
174-
* @deprecated Do not use. Duration is no longer built-in CEL type.
175-
*/
176-
@Deprecated
177-
public static CelConstant ofValue(Duration value) {
178-
return AutoOneOf_CelConstant.durationValue(value);
179-
}
180-
181-
/**
182-
* @deprecated Do not use. Timestamp is no longer built-in CEL type.
183-
*/
184-
@Deprecated
185-
public static CelConstant ofValue(Timestamp value) {
186-
return AutoOneOf_CelConstant.timestampValue(value);
187-
}
188210
}

0 commit comments

Comments
 (0)