Skip to content

Commit 79a3c26

Browse files
l46kokcopybara-github
authored andcommitted
Internal Changes
PiperOrigin-RevId: 574480963
1 parent aeb1d59 commit 79a3c26

30 files changed

+456
-356
lines changed

bundle/src/main/java/dev/cel/bundle/CelBuilder.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
2121
import com.google.protobuf.Descriptors.Descriptor;
2222
import com.google.protobuf.Descriptors.FileDescriptor;
23-
import com.google.protobuf.ExtensionRegistry;
2423
import com.google.protobuf.Message;
2524
import dev.cel.checker.ProtoTypeMask;
2625
import dev.cel.checker.TypeProvider;
@@ -283,13 +282,6 @@ public interface CelBuilder {
283282
@CanIgnoreReturnValue
284283
CelBuilder addRuntimeLibraries(Iterable<CelRuntimeLibrary> libraries);
285284

286-
/**
287-
* Sets a proto ExtensionRegistry to assist with unpacking Any messages containing a proto2
288-
extension field.
289-
*/
290-
@CanIgnoreReturnValue
291-
CelBuilder setExtensionRegistry(ExtensionRegistry extensionRegistry);
292-
293285
/** Construct a new {@code Cel} instance from the provided configuration. */
294286
Cel build();
295287
}

bundle/src/main/java/dev/cel/bundle/CelImpl.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
2626
import com.google.protobuf.Descriptors.Descriptor;
2727
import com.google.protobuf.Descriptors.FileDescriptor;
28-
import com.google.protobuf.ExtensionRegistry;
2928
import com.google.protobuf.Message;
3029
import dev.cel.checker.CelCheckerBuilder;
3130
import dev.cel.checker.ProtoTypeMask;
@@ -340,13 +339,6 @@ public Builder addRuntimeLibraries(Iterable<CelRuntimeLibrary> libraries) {
340339
return this;
341340
}
342341

343-
@Override
344-
public CelBuilder setExtensionRegistry(ExtensionRegistry extensionRegistry) {
345-
checkNotNull(extensionRegistry);
346-
runtimeBuilder.setExtensionRegistry(extensionRegistry);
347-
return this;
348-
}
349-
350342
@Override
351343
public Cel build() {
352344
return new CelImpl(

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,11 @@ java_library(
9191
],
9292
)
9393

94+
# keep sorted
9495
DYNAMIC_PROTO_SOURCES = [
9596
"DynamicProto.java",
9697
"ProtoAdapter.java",
98+
"ProtoRegistryProvider.java",
9799
]
98100

99101
java_library(
@@ -117,12 +119,13 @@ java_library(
117119
],
118120
deps = [
119121
":converter",
120-
":proto_message_factory",
121-
":well_known_proto",
122+
":default_instance_message_factory",
122123
"//:auto_value",
124+
"//common",
123125
"//common:error_codes",
124126
"//common:runtime_exception",
125127
"//common/annotations",
128+
"//common/types:cel_types",
126129
"@cel_spec//proto/cel/expr:expr_java_proto",
127130
"@maven//:com_google_code_findbugs_annotations",
128131
"@maven//:com_google_errorprone_error_prone_annotations",

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,7 @@ public ExtensionRegistry getExtensionRegistry() {
7474
private CombinedDescriptorPool(ImmutableList<CelDescriptorPool> descriptorPools) {
7575
this.descriptorPools = descriptorPools;
7676
// TODO: Combine the extension registry. This will become necessary once we accept
77-
// ExtensionRegistry through runtime builder. Ideally, proto team should open source this
78-
// implementation but we may have to create our own.
77+
// ExtensionRegistry through runtime builder.
7978
this.extensionRegistry =
8079
descriptorPools.stream()
8180
.map(CelDescriptorPool::getExtensionRegistry)

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

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,7 @@ public final class DefaultDescriptorPool implements CelDescriptorPool {
4646

4747
/** A DefaultDescriptorPool instance with just well known types loaded. */
4848
public static final DefaultDescriptorPool INSTANCE =
49-
new DefaultDescriptorPool(
50-
WELL_KNOWN_TYPE_DESCRIPTORS,
51-
ImmutableMultimap.of(),
52-
ExtensionRegistry.getEmptyRegistry());
49+
new DefaultDescriptorPool(WELL_KNOWN_TYPE_DESCRIPTORS, ImmutableMultimap.of());
5350

5451
// K: Fully qualified message type name, V: Message descriptor
5552
private final ImmutableMap<String, Descriptor> descriptorMap;
@@ -58,15 +55,7 @@ public final class DefaultDescriptorPool implements CelDescriptorPool {
5855
// V: Field descriptor for the extension message
5956
private final ImmutableMultimap<String, FieldDescriptor> extensionDescriptorMap;
6057

61-
@SuppressWarnings("Immutable") // ExtensionRegistry is immutable, just not marked as such.
62-
private final ExtensionRegistry extensionRegistry;
63-
6458
public static DefaultDescriptorPool create(CelDescriptors celDescriptors) {
65-
return create(celDescriptors, ExtensionRegistry.getEmptyRegistry());
66-
}
67-
68-
public static DefaultDescriptorPool create(
69-
CelDescriptors celDescriptors, ExtensionRegistry extensionRegistry) {
7059
Map<String, Descriptor> descriptorMap = new HashMap<>(); // Using a hashmap to allow deduping
7160
stream(WellKnownProto.values()).forEach(d -> descriptorMap.put(d.typeName(), d.descriptor()));
7261

@@ -75,9 +64,7 @@ public static DefaultDescriptorPool create(
7564
}
7665

7766
return new DefaultDescriptorPool(
78-
ImmutableMap.copyOf(descriptorMap),
79-
celDescriptors.extensionDescriptors(),
80-
extensionRegistry);
67+
ImmutableMap.copyOf(descriptorMap), celDescriptors.extensionDescriptors());
8168
}
8269

8370
@Override
@@ -96,15 +83,14 @@ public Optional<FieldDescriptor> findExtensionDescriptor(
9683

9784
@Override
9885
public ExtensionRegistry getExtensionRegistry() {
99-
return extensionRegistry;
86+
// TODO: Populate one from runtime builder.
87+
return ExtensionRegistry.getEmptyRegistry();
10088
}
10189

10290
private DefaultDescriptorPool(
10391
ImmutableMap<String, Descriptor> descriptorMap,
104-
ImmutableMultimap<String, FieldDescriptor> extensionDescriptorMap,
105-
ExtensionRegistry extensionRegistry) {
92+
ImmutableMultimap<String, FieldDescriptor> extensionDescriptorMap) {
10693
this.descriptorMap = checkNotNull(descriptorMap);
10794
this.extensionDescriptorMap = checkNotNull(extensionDescriptorMap);
108-
this.extensionRegistry = checkNotNull(extensionRegistry);
10995
}
11096
}

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

Lines changed: 118 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,29 @@
1515
package dev.cel.common.internal;
1616

1717
import static com.google.common.base.Preconditions.checkNotNull;
18+
import static com.google.common.collect.ImmutableMap.toImmutableMap;
19+
import static java.util.Arrays.stream;
1820

21+
import com.google.auto.value.AutoBuilder;
22+
import com.google.common.collect.ImmutableCollection;
23+
import com.google.common.collect.ImmutableMap;
24+
import com.google.common.collect.ImmutableMultimap;
1925
import com.google.errorprone.annotations.CheckReturnValue;
2026
import com.google.errorprone.annotations.Immutable;
2127
import com.google.protobuf.Any;
2228
import com.google.protobuf.ByteString;
29+
import com.google.protobuf.Descriptors.Descriptor;
30+
import com.google.protobuf.Descriptors.FieldDescriptor;
2331
import com.google.protobuf.DynamicMessage;
2432
import com.google.protobuf.InvalidProtocolBufferException;
2533
import com.google.protobuf.Message;
34+
import dev.cel.common.CelDescriptorUtil;
35+
import dev.cel.common.CelDescriptors;
2636
import dev.cel.common.annotations.Internal;
37+
import dev.cel.common.types.CelTypes;
38+
import java.util.Map.Entry;
2739
import java.util.Optional;
40+
import org.jspecify.nullness.Nullable;
2841

2942
/**
3043
* The {@code DynamicProto} class supports the conversion of {@link Any} values to concrete {@code
@@ -36,30 +49,59 @@
3649
@CheckReturnValue
3750
@Internal
3851
public final class DynamicProto {
52+
53+
private static final ImmutableMap<String, Descriptor> WELL_KNOWN_DESCRIPTORS =
54+
stream(ProtoAdapter.WellKnownProto.values())
55+
.collect(toImmutableMap(d -> d.typeName(), d -> d.descriptor()));
56+
57+
private final ImmutableMap<String, Descriptor> dynamicDescriptors;
58+
private final ImmutableMultimap<String, FieldDescriptor> dynamicExtensionDescriptors;
3959
private final ProtoMessageFactory protoMessageFactory;
4060

41-
public static DynamicProto create(ProtoMessageFactory protoMessageFactory) {
42-
return new DynamicProto(protoMessageFactory);
61+
/** {@code ProtoMessageFactory} provides a method to create a protobuf builder objects by name. */
62+
@Immutable
63+
@FunctionalInterface
64+
public interface ProtoMessageFactory {
65+
Message.@Nullable Builder newBuilder(String messageName);
4366
}
4467

45-
DynamicProto(ProtoMessageFactory protoMessageFactory) {
46-
this.protoMessageFactory = checkNotNull(protoMessageFactory);
68+
/** Builder for configuring the {@link DynamicProto}. */
69+
@AutoBuilder(ofClass = DynamicProto.class)
70+
public abstract static class Builder {
71+
72+
/** Sets {@link CelDescriptors} to unpack any message types. */
73+
public abstract Builder setDynamicDescriptors(CelDescriptors celDescriptors);
74+
75+
/** Sets a custom type factory to unpack any message types. */
76+
public abstract Builder setProtoMessageFactory(ProtoMessageFactory factory);
77+
78+
/** Builds a new instance of {@link DynamicProto} */
79+
@CheckReturnValue
80+
public abstract DynamicProto build();
4781
}
4882

49-
/** Attempts to unpack an Any message. */
50-
public Optional<Message> maybeUnpackAny(Message msg) {
51-
try {
52-
Any any =
53-
msg instanceof Any
54-
? (Any) msg
55-
: Any.parseFrom(
56-
msg.toByteString(),
57-
protoMessageFactory.getDescriptorPool().getExtensionRegistry());
58-
59-
return Optional.of(unpack(any));
60-
} catch (InvalidProtocolBufferException e) {
61-
return Optional.empty();
62-
}
83+
public static Builder newBuilder() {
84+
return new AutoBuilder_DynamicProto_Builder()
85+
.setDynamicDescriptors(CelDescriptors.builder().build())
86+
.setProtoMessageFactory((typeName) -> null);
87+
}
88+
89+
DynamicProto(
90+
CelDescriptors dynamicDescriptors,
91+
ProtoMessageFactory protoMessageFactory) {
92+
ImmutableMap<String, Descriptor> messageTypeDescriptorMap =
93+
CelDescriptorUtil.descriptorCollectionToMap(dynamicDescriptors.messageTypeDescriptors());
94+
ImmutableMap<String, Descriptor> filteredDescriptors =
95+
messageTypeDescriptorMap.entrySet().stream()
96+
.filter(e -> !WELL_KNOWN_DESCRIPTORS.containsKey(e.getKey()))
97+
.collect(toImmutableMap(Entry::getKey, Entry::getValue));
98+
this.dynamicDescriptors =
99+
ImmutableMap.<String, Descriptor>builder()
100+
.putAll(WELL_KNOWN_DESCRIPTORS)
101+
.putAll(filteredDescriptors)
102+
.buildOrThrow();
103+
this.dynamicExtensionDescriptors = checkNotNull(dynamicDescriptors.extensionDescriptors());
104+
this.protoMessageFactory = checkNotNull(protoMessageFactory);
63105
}
64106

65107
/**
@@ -78,8 +120,7 @@ public Message unpack(Any any) throws InvalidProtocolBufferException {
78120
String.format("malformed type URL: %s", any.getTypeUrl())));
79121

80122
Message.Builder builder =
81-
protoMessageFactory
82-
.newBuilder(messageTypeName)
123+
newMessageBuilder(messageTypeName)
83124
.orElseThrow(
84125
() ->
85126
new InvalidProtocolBufferException(
@@ -95,7 +136,7 @@ public Message unpack(Any any) throws InvalidProtocolBufferException {
95136
*/
96137
public Message maybeAdaptDynamicMessage(DynamicMessage input) {
97138
Optional<Message.Builder> maybeBuilder =
98-
protoMessageFactory.newBuilder(input.getDescriptorForType().getFullName());
139+
newMessageBuilder(input.getDescriptorForType().getFullName());
99140
if (!maybeBuilder.isPresent() || maybeBuilder.get() instanceof DynamicMessage.Builder) {
100141
// Just return the same input if:
101142
// 1. We didn't get a builder back because there's no descriptor (nothing we can do)
@@ -107,6 +148,61 @@ public Message maybeAdaptDynamicMessage(DynamicMessage input) {
107148
return merge(maybeBuilder.get(), input.toByteString());
108149
}
109150

151+
/**
152+
* This method instantiates a builder for the given {@code typeName} assuming one is configured
153+
* within the descriptor set provided to the {@code DynamicProto} constructor.
154+
*
155+
* <p>When the {@code useLinkedTypes} flag is set, the {@code Message.Builder} returned will be
156+
* the concrete builder instance linked into the binary if it is present; otherwise, the result
157+
* will be a {@code DynamicMessageBuilder}.
158+
*/
159+
public Optional<Message.Builder> newMessageBuilder(String typeName) {
160+
if (!CelTypes.isWellKnownType(typeName)) {
161+
// Check if the message factory can produce a concrete message via custom type factory
162+
// first.
163+
Message.Builder builder = protoMessageFactory.newBuilder(typeName);
164+
if (builder != null) {
165+
return Optional.of(builder);
166+
}
167+
}
168+
169+
Optional<Descriptor> descriptor = maybeGetDescriptor(typeName);
170+
if (!descriptor.isPresent()) {
171+
return Optional.empty();
172+
}
173+
// If the descriptor that's resolved does not match the descriptor instance in the message
174+
// factory, the call to fetch the prototype will return null, and a dynamic proto message
175+
// should be used as a fallback.
176+
Optional<Message> message =
177+
DefaultInstanceMessageFactory.getInstance().getPrototype(descriptor.get());
178+
if (message.isPresent()) {
179+
return Optional.of(message.get().toBuilder());
180+
}
181+
182+
// Fallback to a dynamic proto instance.
183+
return Optional.of(DynamicMessage.newBuilder(descriptor.get()));
184+
}
185+
186+
private Optional<Descriptor> maybeGetDescriptor(String typeName) {
187+
188+
Descriptor descriptor = ProtoRegistryProvider.getTypeRegistry().find(typeName);
189+
return Optional.ofNullable(descriptor != null ? descriptor : dynamicDescriptors.get(typeName));
190+
}
191+
192+
/** Gets the corresponding field descriptor for an extension field on a message. */
193+
public Optional<FieldDescriptor> maybeGetExtensionDescriptor(
194+
Descriptor containingDescriptor, String fieldName) {
195+
196+
String typeName = containingDescriptor.getFullName();
197+
ImmutableCollection<FieldDescriptor> fieldDescriptors =
198+
dynamicExtensionDescriptors.get(typeName);
199+
if (fieldDescriptors.isEmpty()) {
200+
return Optional.empty();
201+
}
202+
203+
return fieldDescriptors.stream().filter(d -> d.getFullName().equals(fieldName)).findFirst();
204+
}
205+
110206
/**
111207
* Merge takes in a Message builder and merges another message bytes into the builder. Some
112208
* example usages are:
@@ -118,9 +214,7 @@ public Message maybeAdaptDynamicMessage(DynamicMessage input) {
118214
*/
119215
private Message merge(Message.Builder builder, ByteString inputBytes) {
120216
try {
121-
return builder
122-
.mergeFrom(inputBytes, protoMessageFactory.getDescriptorPool().getExtensionRegistry())
123-
.build();
217+
return builder.mergeFrom(inputBytes, ProtoRegistryProvider.getExtensionRegistry()).build();
124218
} catch (InvalidProtocolBufferException e) {
125219
throw new AssertionError("Failed to merge input message into the message builder", e);
126220
}

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,53 @@ public final class ProtoAdapter {
143143
stream(WellKnownProto.values())
144144
.collect(toImmutableMap(WellKnownProto::typeName, Function.identity()));
145145

146+
/**
147+
* WellKnownProto types used throughout CEL. These types are specially handled to ensure that
148+
* bidirectional conversion between CEL native values and these well-known types is performed
149+
* consistently across runtimes.
150+
*/
151+
enum WellKnownProto {
152+
JSON_VALUE(Value.getDescriptor()),
153+
JSON_STRUCT_VALUE(Struct.getDescriptor()),
154+
JSON_LIST_VALUE(ListValue.getDescriptor()),
155+
ANY_VALUE(Any.getDescriptor()),
156+
BOOL_VALUE(BoolValue.getDescriptor(), true),
157+
BYTES_VALUE(BytesValue.getDescriptor(), true),
158+
DOUBLE_VALUE(DoubleValue.getDescriptor(), true),
159+
FLOAT_VALUE(FloatValue.getDescriptor(), true),
160+
INT32_VALUE(Int32Value.getDescriptor(), true),
161+
INT64_VALUE(Int64Value.getDescriptor(), true),
162+
STRING_VALUE(StringValue.getDescriptor(), true),
163+
UINT32_VALUE(UInt32Value.getDescriptor(), true),
164+
UINT64_VALUE(UInt64Value.getDescriptor(), true),
165+
DURATION_VALUE(Duration.getDescriptor()),
166+
TIMESTAMP_VALUE(Timestamp.getDescriptor());
167+
168+
private final Descriptor descriptor;
169+
private final boolean isWrapperType;
170+
171+
WellKnownProto(Descriptor descriptor) {
172+
this(descriptor, /* isWrapperType= */ false);
173+
}
174+
175+
WellKnownProto(Descriptor descriptor, boolean isWrapperType) {
176+
this.descriptor = descriptor;
177+
this.isWrapperType = isWrapperType;
178+
}
179+
180+
Descriptor descriptor() {
181+
return descriptor;
182+
}
183+
184+
String typeName() {
185+
return descriptor.getFullName();
186+
}
187+
188+
boolean isWrapperType() {
189+
return isWrapperType;
190+
}
191+
}
192+
146193
private final DynamicProto dynamicProto;
147194
private final boolean enableUnsignedLongs;
148195

0 commit comments

Comments
 (0)