Skip to content

Commit c95eb08

Browse files
l46kokcopybara-github
authored andcommitted
Change WellKnownProto to lookup a descriptor by its type name. Add google.protobuf.Empty and google.protobuf.FieldMask to WKP
PiperOrigin-RevId: 735808419
1 parent 717c25b commit c95eb08

8 files changed

Lines changed: 174 additions & 47 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ java_library(
217217
"//common/annotations",
218218
"@maven//:com_google_guava_guava",
219219
"@maven//:com_google_protobuf_protobuf_java",
220+
"@maven//:org_jspecify_jspecify",
220221
],
221222
)
222223

common/src/main/java/dev/cel/common/internal/DefaultDescriptorPool.java

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,26 @@
2222
import com.google.common.collect.ImmutableMap;
2323
import com.google.common.collect.ImmutableMultimap;
2424
import com.google.errorprone.annotations.Immutable;
25+
import com.google.protobuf.Any;
26+
import com.google.protobuf.BoolValue;
27+
import com.google.protobuf.BytesValue;
2528
import com.google.protobuf.Descriptors.Descriptor;
2629
import com.google.protobuf.Descriptors.FieldDescriptor;
30+
import com.google.protobuf.DoubleValue;
31+
import com.google.protobuf.Duration;
32+
import com.google.protobuf.Empty;
2733
import com.google.protobuf.ExtensionRegistry;
34+
import com.google.protobuf.FieldMask;
35+
import com.google.protobuf.FloatValue;
36+
import com.google.protobuf.Int32Value;
37+
import com.google.protobuf.Int64Value;
38+
import com.google.protobuf.ListValue;
39+
import com.google.protobuf.StringValue;
40+
import com.google.protobuf.Struct;
41+
import com.google.protobuf.Timestamp;
42+
import com.google.protobuf.UInt32Value;
43+
import com.google.protobuf.UInt64Value;
44+
import com.google.protobuf.Value;
2845
import dev.cel.common.CelDescriptors;
2946
import dev.cel.common.annotations.Internal;
3047
import java.util.HashMap;
@@ -40,14 +57,36 @@
4057
@Immutable
4158
@Internal
4259
public final class DefaultDescriptorPool implements CelDescriptorPool {
43-
private static final ImmutableMap<String, Descriptor> WELL_KNOWN_TYPE_DESCRIPTORS =
60+
61+
private static final ImmutableMap<WellKnownProto, Descriptor> WELL_KNOWN_PROTO_TO_DESCRIPTORS =
62+
ImmutableMap.<WellKnownProto, Descriptor>builder()
63+
.put(WellKnownProto.ANY_VALUE, Any.getDescriptor())
64+
.put(WellKnownProto.BOOL_VALUE, BoolValue.getDescriptor())
65+
.put(WellKnownProto.BYTES_VALUE, BytesValue.getDescriptor())
66+
.put(WellKnownProto.DOUBLE_VALUE, DoubleValue.getDescriptor())
67+
.put(WellKnownProto.DURATION, Duration.getDescriptor())
68+
.put(WellKnownProto.FLOAT_VALUE, FloatValue.getDescriptor())
69+
.put(WellKnownProto.INT32_VALUE, Int32Value.getDescriptor())
70+
.put(WellKnownProto.INT64_VALUE, Int64Value.getDescriptor())
71+
.put(WellKnownProto.STRING_VALUE, StringValue.getDescriptor())
72+
.put(WellKnownProto.TIMESTAMP, Timestamp.getDescriptor())
73+
.put(WellKnownProto.UINT32_VALUE, UInt32Value.getDescriptor())
74+
.put(WellKnownProto.UINT64_VALUE, UInt64Value.getDescriptor())
75+
.put(WellKnownProto.JSON_LIST_VALUE, ListValue.getDescriptor())
76+
.put(WellKnownProto.JSON_STRUCT_VALUE, Struct.getDescriptor())
77+
.put(WellKnownProto.JSON_VALUE, Value.getDescriptor())
78+
.put(WellKnownProto.EMPTY, Empty.getDescriptor())
79+
.put(WellKnownProto.FIELD_MASK, FieldMask.getDescriptor())
80+
.buildOrThrow();
81+
82+
private static final ImmutableMap<String, Descriptor> WELL_KNOWN_TYPE_NAME_TO_DESCRIPTORS =
4483
stream(WellKnownProto.values())
45-
.collect(toImmutableMap(WellKnownProto::typeName, WellKnownProto::descriptor));
84+
.collect(toImmutableMap(WellKnownProto::typeName, WELL_KNOWN_PROTO_TO_DESCRIPTORS::get));
4685

4786
/** A DefaultDescriptorPool instance with just well known types loaded. */
4887
public static final DefaultDescriptorPool INSTANCE =
4988
new DefaultDescriptorPool(
50-
WELL_KNOWN_TYPE_DESCRIPTORS,
89+
WELL_KNOWN_TYPE_NAME_TO_DESCRIPTORS,
5190
ImmutableMultimap.of(),
5291
ExtensionRegistry.getEmptyRegistry());
5392

@@ -67,8 +106,8 @@ public static DefaultDescriptorPool create(CelDescriptors celDescriptors) {
67106

68107
public static DefaultDescriptorPool create(
69108
CelDescriptors celDescriptors, ExtensionRegistry extensionRegistry) {
70-
Map<String, Descriptor> descriptorMap = new HashMap<>(); // Using a hashmap to allow deduping
71-
stream(WellKnownProto.values()).forEach(d -> descriptorMap.put(d.typeName(), d.descriptor()));
109+
Map<String, Descriptor> descriptorMap =
110+
new HashMap<>(WELL_KNOWN_TYPE_NAME_TO_DESCRIPTORS); // Using a hashmap to allow deduping
72111

73112
for (Descriptor descriptor : celDescriptors.messageTypeDescriptors()) {
74113
descriptorMap.putIfAbsent(descriptor.getFullName(), descriptor);

common/src/main/java/dev/cel/common/internal/ProtoAdapter.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ public Object adaptProtoToValue(MessageOrBuilder proto) {
157157
// If the proto is not a well-known type, then the input Message is what's expected as the
158158
// output return value.
159159
WellKnownProto wellKnownProto =
160-
WellKnownProto.getByDescriptorName(typeName(proto.getDescriptorForType()));
160+
WellKnownProto.getByTypeName(typeName(proto.getDescriptorForType()));
161161
if (wellKnownProto == null) {
162162
return proto;
163163
}
@@ -335,7 +335,7 @@ private BidiConverter fieldToValueConverter(FieldDescriptor fieldDescriptor) {
335335
*/
336336
@SuppressWarnings("unchecked")
337337
public Optional<Message> adaptValueToProto(Object value, String protoTypeName) {
338-
WellKnownProto wellKnownProto = WellKnownProto.getByDescriptorName(protoTypeName);
338+
WellKnownProto wellKnownProto = WellKnownProto.getByTypeName(protoTypeName);
339339
if (wellKnownProto == null) {
340340
if (value instanceof Message) {
341341
return Optional.of((Message) value);
@@ -376,7 +376,7 @@ public Optional<Message> adaptValueToProto(Object value, String protoTypeName) {
376376
break;
377377
case DOUBLE_VALUE:
378378
return Optional.ofNullable(adaptValueToDouble(value));
379-
case DURATION_VALUE:
379+
case DURATION:
380380
return Optional.of((Duration) value);
381381
case FLOAT_VALUE:
382382
return Optional.ofNullable(adaptValueToFloat(value));
@@ -389,12 +389,14 @@ public Optional<Message> adaptValueToProto(Object value, String protoTypeName) {
389389
return Optional.of(StringValue.of((String) value));
390390
}
391391
break;
392-
case TIMESTAMP_VALUE:
392+
case TIMESTAMP:
393393
return Optional.of((Timestamp) value);
394394
case UINT32_VALUE:
395395
return Optional.ofNullable(adaptValueToUint32(value));
396396
case UINT64_VALUE:
397397
return Optional.ofNullable(adaptValueToUint64(value));
398+
default:
399+
throw new IllegalArgumentException("Unsupported conversion: " + wellKnownProto);
398400
}
399401
return Optional.empty();
400402
}
@@ -579,7 +581,7 @@ private static boolean isWrapperType(FieldDescriptor fieldDescriptor) {
579581
return false;
580582
}
581583
String fieldTypeName = fieldDescriptor.getMessageType().getFullName();
582-
WellKnownProto wellKnownProto = WellKnownProto.getByDescriptorName(fieldTypeName);
584+
WellKnownProto wellKnownProto = WellKnownProto.getByTypeName(fieldTypeName);
583585
return wellKnownProto != null && wellKnownProto.isWrapperType();
584586
}
585587

common/src/main/java/dev/cel/common/internal/WellKnownProto.java

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
import com.google.protobuf.Any;
2222
import com.google.protobuf.BoolValue;
2323
import com.google.protobuf.BytesValue;
24-
import com.google.protobuf.Descriptors.Descriptor;
2524
import com.google.protobuf.DoubleValue;
2625
import com.google.protobuf.Duration;
26+
import com.google.protobuf.Empty;
27+
import com.google.protobuf.FieldMask;
2728
import com.google.protobuf.FloatValue;
2829
import com.google.protobuf.Int32Value;
2930
import com.google.protobuf.Int64Value;
@@ -36,6 +37,7 @@
3637
import com.google.protobuf.Value;
3738
import dev.cel.common.annotations.Internal;
3839
import java.util.function.Function;
40+
import org.jspecify.annotations.Nullable;
3941

4042
/**
4143
* WellKnownProto types used throughout CEL. These types are specially handled to ensure that
@@ -44,24 +46,32 @@
4446
*/
4547
@Internal
4648
public enum WellKnownProto {
47-
JSON_VALUE(Value.getDescriptor()),
48-
JSON_STRUCT_VALUE(Struct.getDescriptor()),
49-
JSON_LIST_VALUE(ListValue.getDescriptor()),
50-
ANY_VALUE(Any.getDescriptor()),
51-
BOOL_VALUE(BoolValue.getDescriptor(), true),
52-
BYTES_VALUE(BytesValue.getDescriptor(), true),
53-
DOUBLE_VALUE(DoubleValue.getDescriptor(), true),
54-
FLOAT_VALUE(FloatValue.getDescriptor(), true),
55-
INT32_VALUE(Int32Value.getDescriptor(), true),
56-
INT64_VALUE(Int64Value.getDescriptor(), true),
57-
STRING_VALUE(StringValue.getDescriptor(), true),
58-
UINT32_VALUE(UInt32Value.getDescriptor(), true),
59-
UINT64_VALUE(UInt64Value.getDescriptor(), true),
60-
DURATION_VALUE(Duration.getDescriptor()),
61-
TIMESTAMP_VALUE(Timestamp.getDescriptor());
62-
63-
private final Descriptor descriptor;
64-
private final boolean isWrapperType;
49+
ANY_VALUE("google.protobuf.Any", Any.class.getName()),
50+
DURATION("google.protobuf.Duration", Duration.class.getName()),
51+
JSON_LIST_VALUE("google.protobuf.ListValue", ListValue.class.getName()),
52+
JSON_STRUCT_VALUE("google.protobuf.Struct", Struct.class.getName()),
53+
JSON_VALUE("google.protobuf.Value", Value.class.getName()),
54+
TIMESTAMP("google.protobuf.Timestamp", Timestamp.class.getName()),
55+
// Wrapper types
56+
FLOAT_VALUE("google.protobuf.FloatValue", FloatValue.class.getName(), /* isWrapperType= */ true),
57+
INT32_VALUE("google.protobuf.Int32Value", Int32Value.class.getName(), /* isWrapperType= */ true),
58+
INT64_VALUE("google.protobuf.Int64Value", Int64Value.class.getName(), /* isWrapperType= */ true),
59+
STRING_VALUE(
60+
"google.protobuf.StringValue", StringValue.class.getName(), /* isWrapperType= */ true),
61+
BOOL_VALUE("google.protobuf.BoolValue", BoolValue.class.getName(), /* isWrapperType= */ true),
62+
BYTES_VALUE("google.protobuf.BytesValue", BytesValue.class.getName(), /* isWrapperType= */ true),
63+
DOUBLE_VALUE(
64+
"google.protobuf.DoubleValue", DoubleValue.class.getName(), /* isWrapperType= */ true),
65+
UINT32_VALUE(
66+
"google.protobuf.UInt32Value", UInt32Value.class.getName(), /* isWrapperType= */ true),
67+
UINT64_VALUE(
68+
"google.protobuf.UInt64Value", UInt64Value.class.getName(), /* isWrapperType= */ true),
69+
// These aren't explicitly called out as wrapper types in the spec, but behave like one, because
70+
// they are still converted into an equivalent primitive type.
71+
72+
EMPTY("google.protobuf.Empty", Empty.class.getName(), /* isWrapperType= */ true),
73+
FIELD_MASK("google.protobuf.FieldMask", FieldMask.class.getName(), /* isWrapperType= */ true),
74+
;
6575

6676
private static final ImmutableMap<String, WellKnownProto> WELL_KNOWN_PROTO_MAP;
6777

@@ -71,28 +81,42 @@ public enum WellKnownProto {
7181
.collect(toImmutableMap(WellKnownProto::typeName, Function.identity()));
7282
}
7383

74-
WellKnownProto(Descriptor descriptor) {
75-
this(descriptor, /* isWrapperType= */ false);
84+
private final String wellKnownProtoFullName;
85+
private final String javaClassName;
86+
private final boolean isWrapperType;
87+
88+
public String typeName() {
89+
return wellKnownProtoFullName;
7690
}
7791

78-
WellKnownProto(Descriptor descriptor, boolean isWrapperType) {
79-
this.descriptor = descriptor;
80-
this.isWrapperType = isWrapperType;
92+
public String javaClassName() {
93+
return this.javaClassName;
8194
}
8295

83-
public Descriptor descriptor() {
84-
return descriptor;
96+
public static @Nullable WellKnownProto getByTypeName(String typeName) {
97+
return WELL_KNOWN_PROTO_MAP.get(typeName);
8598
}
8699

87-
public String typeName() {
88-
return descriptor.getFullName();
100+
public static boolean isWrapperType(String typeName) {
101+
WellKnownProto wellKnownProto = getByTypeName(typeName);
102+
if (wellKnownProto == null) {
103+
return false;
104+
}
105+
106+
return wellKnownProto.isWrapperType();
89107
}
90108

91109
public boolean isWrapperType() {
92110
return isWrapperType;
93111
}
94112

95-
public static WellKnownProto getByDescriptorName(String name) {
96-
return WELL_KNOWN_PROTO_MAP.get(name);
113+
WellKnownProto(String wellKnownProtoFullName, String javaClassName) {
114+
this(wellKnownProtoFullName, javaClassName, /* isWrapperType= */ false);
115+
}
116+
117+
WellKnownProto(String wellKnownProtoFullName, String javaClassName, boolean isWrapperType) {
118+
this.wellKnownProtoFullName = wellKnownProtoFullName;
119+
this.javaClassName = javaClassName;
120+
this.isWrapperType = isWrapperType;
97121
}
98122
}

common/src/main/java/dev/cel/common/values/ProtoCelValueConverter.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public CelValue fromProtoMessageToCelValue(MessageOrBuilder message) {
107107
}
108108

109109
WellKnownProto wellKnownProto =
110-
WellKnownProto.getByDescriptorName(message.getDescriptorForType().getFullName());
110+
WellKnownProto.getByTypeName(message.getDescriptorForType().getFullName());
111111
if (wellKnownProto == null) {
112112
return ProtoMessageValue.create((Message) message, celDescriptorPool, this);
113113
}
@@ -128,10 +128,10 @@ public CelValue fromProtoMessageToCelValue(MessageOrBuilder message) {
128128
return adaptJsonStructToCelValue((Struct) message);
129129
case JSON_LIST_VALUE:
130130
return adaptJsonListToCelValue((com.google.protobuf.ListValue) message);
131-
case DURATION_VALUE:
131+
case DURATION:
132132
return DurationValue.create(
133133
TimeUtils.toJavaDuration((com.google.protobuf.Duration) message));
134-
case TIMESTAMP_VALUE:
134+
case TIMESTAMP:
135135
return TimestampValue.create(TimeUtils.toJavaInstant((Timestamp) message));
136136
case BOOL_VALUE:
137137
return fromJavaPrimitiveToCelValue(((BoolValue) message).getValue());
@@ -154,10 +154,10 @@ public CelValue fromProtoMessageToCelValue(MessageOrBuilder message) {
154154
case UINT64_VALUE:
155155
return UintValue.create(
156156
((UInt64Value) message).getValue(), celOptions.enableUnsignedLongs());
157+
default:
158+
throw new UnsupportedOperationException(
159+
"Unsupported message to CelValue conversion - " + message);
157160
}
158-
159-
throw new UnsupportedOperationException(
160-
"Unsupported message to CelValue conversion - " + message);
161161
}
162162

163163
/**

common/src/test/java/dev/cel/common/internal/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ java_library(
2626
"//common/internal:errors",
2727
"//common/internal:proto_equality",
2828
"//common/internal:proto_message_factory",
29+
"//common/internal:well_known_proto",
2930
"//common/src/test/resources:default_instance_message_test_protos_java_proto",
3031
"//common/src/test/resources:multi_file_java_proto",
3132
"//common/src/test/resources:service_conflicting_name_java_proto",
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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.common.internal;
16+
17+
import static com.google.common.truth.Truth.assertThat;
18+
19+
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
20+
import com.google.testing.junit.testparameterinjector.TestParameters;
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
24+
@RunWith(TestParameterInjector.class)
25+
public class WellKnownProtoTest {
26+
27+
@Test
28+
@TestParameters("{typeName: 'google.protobuf.FloatValue'}")
29+
@TestParameters("{typeName: 'google.protobuf.Int32Value'}")
30+
@TestParameters("{typeName: 'google.protobuf.Int64Value'}")
31+
@TestParameters("{typeName: 'google.protobuf.StringValue'}")
32+
@TestParameters("{typeName: 'google.protobuf.BoolValue'}")
33+
@TestParameters("{typeName: 'google.protobuf.BytesValue'}")
34+
@TestParameters("{typeName: 'google.protobuf.DoubleValue'}")
35+
@TestParameters("{typeName: 'google.protobuf.UInt32Value'}")
36+
@TestParameters("{typeName: 'google.protobuf.UInt64Value'}")
37+
@TestParameters("{typeName: 'google.protobuf.Empty'}")
38+
@TestParameters("{typeName: 'google.protobuf.FieldMask'}")
39+
public void isWrapperType_withTypeName_true(String typeName) {
40+
assertThat(WellKnownProto.isWrapperType(typeName)).isTrue();
41+
}
42+
43+
@Test
44+
@TestParameters("{typeName: 'not.wellknown.type'}")
45+
@TestParameters("{typeName: 'google.protobuf.Any'}")
46+
@TestParameters("{typeName: 'google.protobuf.Duration'}")
47+
@TestParameters("{typeName: 'google.protobuf.ListValue'}")
48+
@TestParameters("{typeName: 'google.protobuf.Struct'}")
49+
@TestParameters("{typeName: 'google.protobuf.Value'}")
50+
@TestParameters("{typeName: 'google.protobuf.Timestamp'}")
51+
public void isWrapperType_withTypeName_false(String typeName) {
52+
assertThat(WellKnownProto.isWrapperType(typeName)).isFalse();
53+
}
54+
55+
@Test
56+
public void getJavaClassName() {
57+
assertThat(WellKnownProto.ANY_VALUE.javaClassName()).isEqualTo("com.google.protobuf.Any");
58+
}
59+
}

runtime/src/test/java/dev/cel/runtime/DescriptorMessageProviderTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ public void createMessage_wellKnownType_withCustomMessageProvider(
207207
return;
208208
}
209209

210-
Descriptor wellKnownDescriptor = wellKnownProto.descriptor();
210+
Descriptor wellKnownDescriptor =
211+
DefaultDescriptorPool.INSTANCE.findDescriptor(wellKnownProto.typeName()).get();
211212
DescriptorMessageProvider messageProvider =
212213
new DescriptorMessageProvider(
213214
msgName ->

0 commit comments

Comments
 (0)