diff --git a/common/internal/BUILD.bazel b/common/internal/BUILD.bazel
index f52cfc36c..95b367684 100644
--- a/common/internal/BUILD.bazel
+++ b/common/internal/BUILD.bazel
@@ -35,7 +35,6 @@ java_library(
java_library(
name = "errors",
- visibility = ["//visibility:public"],
exports = ["//common/src/main/java/dev/cel/common/internal:errors"],
)
@@ -43,3 +42,28 @@ java_library(
name = "env_visitor",
exports = ["//common/src/main/java/dev/cel/common/internal:env_visitor"],
)
+
+java_library(
+ name = "default_instance_message_factory",
+ exports = ["//common/src/main/java/dev/cel/common/internal:default_instance_message_factory"],
+)
+
+java_library(
+ name = "well_known_proto",
+ exports = ["//common/src/main/java/dev/cel/common/internal:well_known_proto"],
+)
+
+java_library(
+ name = "proto_message_factory",
+ exports = ["//common/src/main/java/dev/cel/common/internal:proto_message_factory"],
+)
+
+java_library(
+ name = "default_message_factory",
+ exports = ["//common/src/main/java/dev/cel/common/internal:default_message_factory"],
+)
+
+java_library(
+ name = "cel_descriptor_pools",
+ exports = ["//common/src/main/java/dev/cel/common/internal:cel_descriptor_pools"],
+)
diff --git a/common/src/main/java/dev/cel/common/internal/BUILD.bazel b/common/src/main/java/dev/cel/common/internal/BUILD.bazel
index e345633a2..104a82279 100644
--- a/common/src/main/java/dev/cel/common/internal/BUILD.bazel
+++ b/common/src/main/java/dev/cel/common/internal/BUILD.bazel
@@ -24,6 +24,13 @@ ENV_VISITOR_SOURCES = [
"EnvVisitor.java",
]
+# keep sorted
+CEL_DESCRIPTOR_POOL_SOURCES = [
+ "CelDescriptorPool.java",
+ "CombinedDescriptorPool.java",
+ "DefaultDescriptorPool.java",
+]
+
java_library(
name = "internal",
srcs = INTERNAL_SOURCES,
@@ -86,12 +93,25 @@ java_library(
# keep sorted
DYNAMIC_PROTO_SOURCES = [
- "DefaultInstanceMessageFactory.java",
"DynamicProto.java",
"ProtoAdapter.java",
"ProtoRegistryProvider.java",
]
+java_library(
+ name = "default_instance_message_factory",
+ srcs = ["DefaultInstanceMessageFactory.java"],
+ tags = [
+ ],
+ deps = [
+ "//common/annotations",
+ "@maven//:com_google_guava_guava",
+ "@maven//:com_google_protobuf_protobuf_java",
+ ],
+)
+
+# keep sorted
+
java_library(
name = "dynamic_proto",
srcs = DYNAMIC_PROTO_SOURCES,
@@ -99,6 +119,7 @@ java_library(
],
deps = [
":converter",
+ ":default_instance_message_factory",
"//:auto_value",
"//common",
"//common:error_codes",
@@ -155,3 +176,56 @@ java_library(
"@cel_spec//proto/cel/expr:expr_java_proto",
],
)
+
+java_library(
+ name = "well_known_proto",
+ srcs = ["WellKnownProto.java"],
+ tags = [
+ ],
+ deps = [
+ "//common/annotations",
+ "@maven//:com_google_protobuf_protobuf_java",
+ ],
+)
+
+java_library(
+ name = "default_message_factory",
+ srcs = ["DefaultMessageFactory.java"],
+ tags = [
+ ],
+ deps = [
+ ":cel_descriptor_pools",
+ ":default_instance_message_factory",
+ ":proto_message_factory",
+ "//common/annotations",
+ "@maven//:com_google_protobuf_protobuf_java",
+ ],
+)
+
+java_library(
+ name = "proto_message_factory",
+ srcs = ["ProtoMessageFactory.java"],
+ tags = [
+ ],
+ deps = [
+ ":cel_descriptor_pools",
+ "@maven//:com_google_errorprone_error_prone_annotations",
+ "@maven//:com_google_guava_guava",
+ "@maven//:com_google_protobuf_protobuf_java",
+ ],
+)
+
+java_library(
+ name = "cel_descriptor_pools",
+ srcs = CEL_DESCRIPTOR_POOL_SOURCES,
+ tags = [
+ ],
+ deps = [
+ ":well_known_proto",
+ "//common",
+ "//common/annotations",
+ "@maven//:com_google_errorprone_error_prone_annotations",
+ "@maven//:com_google_guava_guava",
+ "@maven//:com_google_protobuf_protobuf_java",
+ ],
+)
diff --git a/common/src/main/java/dev/cel/common/internal/CelDescriptorPool.java b/common/src/main/java/dev/cel/common/internal/CelDescriptorPool.java
new file mode 100644
index 000000000..b2c0533b1
--- /dev/null
+++ b/common/src/main/java/dev/cel/common/internal/CelDescriptorPool.java
@@ -0,0 +1,46 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.common.internal;
+
+import com.google.errorprone.annotations.Immutable;
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.ExtensionRegistry;
+import dev.cel.common.annotations.Internal;
+import java.util.Optional;
+
+/**
+ * {@link CelDescriptorPool} allows lookup of descriptors for message types and field descriptors
+ * for Proto2 extension messages.
+ *
+ *
CEL Library Internals. Do Not Use.
+ */
+@Immutable
+@Internal
+public interface CelDescriptorPool {
+
+ /** Finds the descriptor by fully qualified message type. */
+ Optional findDescriptor(String name);
+
+ /** Finds the corresponding field descriptor for an extension field on a message. */
+ Optional findExtensionDescriptor(
+ Descriptor containingDescriptor, String fieldName);
+
+ /**
+ * Retrieves the registered extension registry. This is specifically needed to handle unpacking
+ * Any messages containing Proto2 extension messages.
+ */
+ ExtensionRegistry getExtensionRegistry();
+}
diff --git a/common/src/main/java/dev/cel/common/internal/CombinedDescriptorPool.java b/common/src/main/java/dev/cel/common/internal/CombinedDescriptorPool.java
new file mode 100644
index 000000000..975e5bdbf
--- /dev/null
+++ b/common/src/main/java/dev/cel/common/internal/CombinedDescriptorPool.java
@@ -0,0 +1,85 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.common.internal;
+
+import com.google.common.collect.ImmutableList;
+import com.google.errorprone.annotations.Immutable;
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.ExtensionRegistry;
+import dev.cel.common.annotations.Internal;
+import java.util.Optional;
+
+/**
+ * The {@link CombinedDescriptorPool} takes one or more {@link CelDescriptorPool} instances and
+ * supports descriptor lookups in the order the descriptor pools are added to the constructor.
+ *
+ * CEL Library Internals. Do Not Use.
+ */
+@Immutable
+@Internal
+public final class CombinedDescriptorPool implements CelDescriptorPool {
+ private final ImmutableList descriptorPools;
+
+ @SuppressWarnings("Immutable") // ExtensionRegistry is immutable, just not marked as such.
+ private final ExtensionRegistry extensionRegistry;
+
+ public static CombinedDescriptorPool create(ImmutableList descriptorPools) {
+ return new CombinedDescriptorPool(descriptorPools);
+ }
+
+ @Override
+ public Optional findDescriptor(String name) {
+ for (CelDescriptorPool descriptorPool : descriptorPools) {
+ Optional maybeDescriptor = descriptorPool.findDescriptor(name);
+ if (maybeDescriptor.isPresent()) {
+ return maybeDescriptor;
+ }
+ }
+
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional findExtensionDescriptor(
+ Descriptor containingDescriptor, String fieldName) {
+ for (CelDescriptorPool descriptorPool : descriptorPools) {
+ Optional maybeExtensionDescriptor =
+ descriptorPool.findExtensionDescriptor(containingDescriptor, fieldName);
+ if (maybeExtensionDescriptor.isPresent()) {
+ return maybeExtensionDescriptor;
+ }
+ }
+
+ return Optional.empty();
+ }
+
+ @Override
+ public ExtensionRegistry getExtensionRegistry() {
+ return extensionRegistry;
+ }
+
+ private CombinedDescriptorPool(ImmutableList descriptorPools) {
+ this.descriptorPools = descriptorPools;
+ // TODO: Combine the extension registry. This will become necessary once we accept
+ // ExtensionRegistry through runtime builder.
+ this.extensionRegistry =
+ descriptorPools.stream()
+ .map(CelDescriptorPool::getExtensionRegistry)
+ .filter(e -> !e.equals(ExtensionRegistry.getEmptyRegistry()))
+ .findFirst()
+ .orElse(ExtensionRegistry.getEmptyRegistry());
+ }
+}
diff --git a/common/src/main/java/dev/cel/common/internal/DefaultDescriptorPool.java b/common/src/main/java/dev/cel/common/internal/DefaultDescriptorPool.java
new file mode 100644
index 000000000..66dbf2e1e
--- /dev/null
+++ b/common/src/main/java/dev/cel/common/internal/DefaultDescriptorPool.java
@@ -0,0 +1,96 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.common.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static java.util.Arrays.stream;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.errorprone.annotations.Immutable;
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.ExtensionRegistry;
+import dev.cel.common.CelDescriptors;
+import dev.cel.common.annotations.Internal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * A descriptor pool that has descriptors pre-loaded for well-known types defined by {@link
+ * WellKnownProto}.
+ *
+ * CEL Library Internals. Do Not Use.
+ */
+@Immutable
+@Internal
+public final class DefaultDescriptorPool implements CelDescriptorPool {
+ private static final ImmutableMap WELL_KNOWN_TYPE_DESCRIPTORS =
+ stream(WellKnownProto.values())
+ .collect(toImmutableMap(WellKnownProto::typeName, WellKnownProto::descriptor));
+
+ /** A DefaultDescriptorPool instance with just well known types loaded. */
+ public static final DefaultDescriptorPool INSTANCE =
+ new DefaultDescriptorPool(WELL_KNOWN_TYPE_DESCRIPTORS, ImmutableMultimap.of());
+
+ // K: Fully qualified message type name, V: Message descriptor
+ private final ImmutableMap descriptorMap;
+
+ // K: Fully qualified message type name (of containing descriptor)
+ // V: Field descriptor for the extension message
+ private final ImmutableMultimap extensionDescriptorMap;
+
+ public static DefaultDescriptorPool create(CelDescriptors celDescriptors) {
+ Map descriptorMap = new HashMap<>(); // Using a hashmap to allow deduping
+ stream(WellKnownProto.values()).forEach(d -> descriptorMap.put(d.typeName(), d.descriptor()));
+
+ for (Descriptor descriptor : celDescriptors.messageTypeDescriptors()) {
+ descriptorMap.putIfAbsent(descriptor.getFullName(), descriptor);
+ }
+
+ return new DefaultDescriptorPool(
+ ImmutableMap.copyOf(descriptorMap), celDescriptors.extensionDescriptors());
+ }
+
+ @Override
+ public Optional findDescriptor(String name) {
+ return Optional.ofNullable(descriptorMap.get(name));
+ }
+
+ @Override
+ public Optional findExtensionDescriptor(
+ Descriptor containingDescriptor, String fieldName) {
+ String typeName = containingDescriptor.getFullName();
+ ImmutableCollection fieldDescriptors = extensionDescriptorMap.get(typeName);
+
+ return fieldDescriptors.stream().filter(d -> d.getFullName().equals(fieldName)).findFirst();
+ }
+
+ @Override
+ public ExtensionRegistry getExtensionRegistry() {
+ // TODO: Populate one from runtime builder.
+ return ExtensionRegistry.getEmptyRegistry();
+ }
+
+ private DefaultDescriptorPool(
+ ImmutableMap descriptorMap,
+ ImmutableMultimap extensionDescriptorMap) {
+ this.descriptorMap = checkNotNull(descriptorMap);
+ this.extensionDescriptorMap = checkNotNull(extensionDescriptorMap);
+ }
+}
diff --git a/common/src/main/java/dev/cel/common/internal/DefaultMessageFactory.java b/common/src/main/java/dev/cel/common/internal/DefaultMessageFactory.java
new file mode 100644
index 000000000..381a5de9e
--- /dev/null
+++ b/common/src/main/java/dev/cel/common/internal/DefaultMessageFactory.java
@@ -0,0 +1,60 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.common.internal;
+
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.DynamicMessage;
+import com.google.protobuf.Message;
+import dev.cel.common.annotations.Internal;
+import java.util.Optional;
+
+/** DefaultMessageFactory produces {@link Message.Builder} instances by protobuf name. */
+@Internal
+public final class DefaultMessageFactory implements ProtoMessageFactory {
+ private final CelDescriptorPool celDescriptorPool;
+
+ public static DefaultMessageFactory create(CelDescriptorPool celDescriptorPool) {
+ return new DefaultMessageFactory(celDescriptorPool);
+ }
+
+ @Override
+ public CelDescriptorPool getDescriptorPool() {
+ return celDescriptorPool;
+ }
+
+ @Override
+ public Optional newBuilder(String messageName) {
+ Optional descriptor = celDescriptorPool.findDescriptor(messageName);
+ if (!descriptor.isPresent()) {
+ return Optional.empty();
+ }
+
+ // If the descriptor that's resolved does not match the descriptor instance in the message
+ // factory, the call to fetch the prototype will return null, and a dynamic proto message
+ // should be used as a fallback.
+ Optional message =
+ DefaultInstanceMessageFactory.getInstance().getPrototype(descriptor.get());
+
+ if (message.isPresent()) {
+ return message.map(Message::toBuilder);
+ }
+
+ return Optional.of(DynamicMessage.newBuilder(descriptor.get()));
+ }
+
+ private DefaultMessageFactory(CelDescriptorPool celDescriptorPool) {
+ this.celDescriptorPool = celDescriptorPool;
+ }
+}
diff --git a/common/src/main/java/dev/cel/common/internal/ProtoMessageFactory.java b/common/src/main/java/dev/cel/common/internal/ProtoMessageFactory.java
new file mode 100644
index 000000000..d7719ff4f
--- /dev/null
+++ b/common/src/main/java/dev/cel/common/internal/ProtoMessageFactory.java
@@ -0,0 +1,63 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.common.internal;
+
+import com.google.common.collect.ImmutableList;
+import com.google.errorprone.annotations.Immutable;
+import com.google.protobuf.Message;
+import java.util.Optional;
+
+/** {@code ProtoMessageFactory} provides a method to create a protobuf builder objects by name. */
+@Immutable
+@FunctionalInterface
+public interface ProtoMessageFactory {
+
+ /**
+ * Constructs a new {@link Message.Builder} for a fully qualified proto message type. An empty
+ * result is returned if a descriptor is missing for the message type name.
+ */
+ Optional newBuilder(String messageName);
+
+ /** Gets the underlying descriptor pool used to construct proto messages. */
+ default CelDescriptorPool getDescriptorPool() {
+ return DefaultDescriptorPool.INSTANCE;
+ }
+
+ /**
+ * The {@link CombinedMessageFactory} takes one or more {@link ProtoMessageFactory} instances and
+ * attempts to create a {@code Message.Builder} instance for a given message name by calling each
+ * message factory in the order that they are provided to the constructor.
+ */
+ @Immutable
+ final class CombinedMessageFactory implements ProtoMessageFactory {
+
+ private final ImmutableList messageFactories;
+
+ public CombinedMessageFactory(Iterable messageFactories) {
+ this.messageFactories = ImmutableList.copyOf(messageFactories);
+ }
+
+ @Override
+ public Optional newBuilder(String messageName) {
+ for (ProtoMessageFactory messageFactory : messageFactories) {
+ Optional builder = messageFactory.newBuilder(messageName);
+ if (builder.isPresent()) {
+ return builder;
+ }
+ }
+ return Optional.empty();
+ }
+ }
+}
diff --git a/common/src/main/java/dev/cel/common/internal/WellKnownProto.java b/common/src/main/java/dev/cel/common/internal/WellKnownProto.java
new file mode 100644
index 000000000..f3520f447
--- /dev/null
+++ b/common/src/main/java/dev/cel/common/internal/WellKnownProto.java
@@ -0,0 +1,81 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.common.internal;
+
+import com.google.protobuf.Any;
+import com.google.protobuf.BoolValue;
+import com.google.protobuf.BytesValue;
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.DoubleValue;
+import com.google.protobuf.Duration;
+import com.google.protobuf.FloatValue;
+import com.google.protobuf.Int32Value;
+import com.google.protobuf.Int64Value;
+import com.google.protobuf.ListValue;
+import com.google.protobuf.StringValue;
+import com.google.protobuf.Struct;
+import com.google.protobuf.Timestamp;
+import com.google.protobuf.UInt32Value;
+import com.google.protobuf.UInt64Value;
+import com.google.protobuf.Value;
+import dev.cel.common.annotations.Internal;
+
+/**
+ * WellKnownProto types used throughout CEL. These types are specially handled to ensure that
+ * bidirectional conversion between CEL native values and these well-known types is performed
+ * consistently across runtimes.
+ */
+@Internal
+public enum WellKnownProto {
+ JSON_VALUE(Value.getDescriptor()),
+ JSON_STRUCT_VALUE(Struct.getDescriptor()),
+ JSON_LIST_VALUE(ListValue.getDescriptor()),
+ ANY_VALUE(Any.getDescriptor()),
+ BOOL_VALUE(BoolValue.getDescriptor(), true),
+ BYTES_VALUE(BytesValue.getDescriptor(), true),
+ DOUBLE_VALUE(DoubleValue.getDescriptor(), true),
+ FLOAT_VALUE(FloatValue.getDescriptor(), true),
+ INT32_VALUE(Int32Value.getDescriptor(), true),
+ INT64_VALUE(Int64Value.getDescriptor(), true),
+ STRING_VALUE(StringValue.getDescriptor(), true),
+ UINT32_VALUE(UInt32Value.getDescriptor(), true),
+ UINT64_VALUE(UInt64Value.getDescriptor(), true),
+ DURATION_VALUE(Duration.getDescriptor()),
+ TIMESTAMP_VALUE(Timestamp.getDescriptor());
+
+ private final Descriptor descriptor;
+ private final boolean isWrapperType;
+
+ WellKnownProto(Descriptor descriptor) {
+ this(descriptor, /* isWrapperType= */ false);
+ }
+
+ WellKnownProto(Descriptor descriptor, boolean isWrapperType) {
+ this.descriptor = descriptor;
+ this.isWrapperType = isWrapperType;
+ }
+
+ public Descriptor descriptor() {
+ return descriptor;
+ }
+
+ public String typeName() {
+ return descriptor.getFullName();
+ }
+
+ boolean isWrapperType() {
+ return isWrapperType;
+ }
+}
diff --git a/common/src/test/java/dev/cel/common/internal/BUILD.bazel b/common/src/test/java/dev/cel/common/internal/BUILD.bazel
index 846219e42..c5b37578b 100644
--- a/common/src/test/java/dev/cel/common/internal/BUILD.bazel
+++ b/common/src/test/java/dev/cel/common/internal/BUILD.bazel
@@ -15,11 +15,18 @@ java_library(
"//common:options",
"//common/ast",
"//common/internal",
+ "//common/internal:cel_descriptor_pools",
"//common/internal:comparison_functions",
"//common/internal:converter",
+ "//common/internal:default_instance_message_factory",
+ "//common/internal:default_message_factory",
"//common/internal:dynamic_proto",
"//common/internal:errors",
"//common/internal:proto_equality",
+ "//common/internal:proto_message_factory",
+ "//common/resources/testdata/proto2:messages_extensions_proto2_java_proto",
+ "//common/resources/testdata/proto2:messages_proto2_java_proto",
+ "//common/resources/testdata/proto2:test_all_types_java_proto",
"//common/resources/testdata/proto3:test_all_types_java_proto",
"//common/src/test/resources:default_instance_message_test_protos_java_proto",
"//common/src/test/resources:multi_file_java_proto",
diff --git a/common/src/test/java/dev/cel/common/internal/CombinedDescriptorPoolTest.java b/common/src/test/java/dev/cel/common/internal/CombinedDescriptorPoolTest.java
new file mode 100644
index 000000000..07f33fc8b
--- /dev/null
+++ b/common/src/test/java/dev/cel/common/internal/CombinedDescriptorPoolTest.java
@@ -0,0 +1,109 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.common.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.ExtensionRegistry;
+import com.google.protobuf.Value;
+import dev.cel.common.CelDescriptorUtil;
+import dev.cel.testing.testdata.proto2.MessagesProto2Extensions;
+import dev.cel.testing.testdata.proto2.Proto2Message;
+import dev.cel.testing.testdata.proto2.TestAllTypesProto.TestAllTypes;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class CombinedDescriptorPoolTest {
+
+ @Test
+ public void findDescriptor_descriptorReturnedFromBothPool() {
+ CelDescriptorPool dynamicDescriptorPool =
+ DefaultDescriptorPool.create(
+ CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(
+ TestAllTypes.getDescriptor().getFile()));
+ CombinedDescriptorPool combinedDescriptorPool =
+ CombinedDescriptorPool.create(
+ ImmutableList.of(DefaultDescriptorPool.INSTANCE, dynamicDescriptorPool));
+
+ assertThat(combinedDescriptorPool.findDescriptor(Value.getDescriptor().getFullName()))
+ .hasValue(
+ Value.getDescriptor()); // Retrieved from default descriptor pool (well-known-type)
+ assertThat(combinedDescriptorPool.findDescriptor(TestAllTypes.getDescriptor().getFullName()))
+ .hasValue(TestAllTypes.getDescriptor()); // Retrieved from the dynamic descriptor pool.
+ }
+
+ @Test
+ public void findDescriptor_returnsEmpty() {
+ CelDescriptorPool descriptorPool =
+ DefaultDescriptorPool.create(
+ CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(
+ TestAllTypes.getDescriptor().getFile()));
+ CombinedDescriptorPool combinedDescriptorPool =
+ CombinedDescriptorPool.create(
+ ImmutableList.of(DefaultDescriptorPool.INSTANCE, descriptorPool));
+
+ assertThat(combinedDescriptorPool.findDescriptor("bogus")).isEmpty();
+ }
+
+ @Test
+ public void findExtensionDescriptor_success() {
+ CelDescriptorPool dynamicDescriptorPool =
+ DefaultDescriptorPool.create(
+ CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(
+ MessagesProto2Extensions.getDescriptor().getFile()));
+ CombinedDescriptorPool combinedDescriptorPool =
+ CombinedDescriptorPool.create(
+ ImmutableList.of(DefaultDescriptorPool.INSTANCE, dynamicDescriptorPool));
+
+ Optional fieldDescriptor =
+ combinedDescriptorPool.findExtensionDescriptor(
+ Proto2Message.getDescriptor(), "dev.cel.testing.testdata.proto2.test_all_types_ext");
+
+ assertThat(fieldDescriptor).isPresent();
+ assertThat(fieldDescriptor.get().isExtension()).isTrue();
+ assertThat(fieldDescriptor.get().getFullName())
+ .isEqualTo("dev.cel.testing.testdata.proto2.test_all_types_ext");
+ }
+
+ @Test
+ public void findExtensionDescriptor_returnsEmpty() {
+ CelDescriptorPool dynamicDescriptorPool =
+ DefaultDescriptorPool.create(
+ CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(
+ MessagesProto2Extensions.getDescriptor().getFile()));
+ CombinedDescriptorPool combinedDescriptorPool =
+ CombinedDescriptorPool.create(
+ ImmutableList.of(DefaultDescriptorPool.INSTANCE, dynamicDescriptorPool));
+
+ assertThat(
+ combinedDescriptorPool.findExtensionDescriptor(TestAllTypes.getDescriptor(), "bogus"))
+ .isEmpty();
+ }
+
+ @Test
+ public void getExtensionRegistry_onDefaultPool_returnsEmptyRegistry() {
+ CombinedDescriptorPool combinedDescriptorPool =
+ CombinedDescriptorPool.create(ImmutableList.of(DefaultDescriptorPool.INSTANCE));
+
+ assertThat(combinedDescriptorPool.getExtensionRegistry())
+ .isEqualTo(ExtensionRegistry.getEmptyRegistry());
+ }
+}
diff --git a/common/src/test/java/dev/cel/common/internal/DefaultMessageFactoryTest.java b/common/src/test/java/dev/cel/common/internal/DefaultMessageFactoryTest.java
new file mode 100644
index 000000000..68f104685
--- /dev/null
+++ b/common/src/test/java/dev/cel/common/internal/DefaultMessageFactoryTest.java
@@ -0,0 +1,121 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.common.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.base.Ascii;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.Resources;
+import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
+import com.google.protobuf.Descriptors.FileDescriptor;
+import com.google.protobuf.DynamicMessage;
+import com.google.protobuf.TextFormat;
+import com.google.protobuf.Value;
+import dev.cel.common.CelDescriptorUtil;
+import dev.cel.common.CelDescriptors;
+import dev.cel.common.internal.ProtoMessageFactory.CombinedMessageFactory;
+import dev.cel.testing.testdata.proto2.TestAllTypesProto.TestAllTypes;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class DefaultMessageFactoryTest {
+
+ @Test
+ public void newBuilder_wellKnownType_producesNewMessage() {
+ DefaultMessageFactory messageFactory =
+ DefaultMessageFactory.create(DefaultDescriptorPool.INSTANCE);
+
+ Value.Builder valueBuilder =
+ (Value.Builder) messageFactory.newBuilder("google.protobuf.Value").get();
+
+ assertThat(valueBuilder.setStringValue("hello").build())
+ .isEqualTo(Value.newBuilder().setStringValue("hello").build());
+ }
+
+ @Test
+ public void newBuilder_withDescriptor_producesNewMessageBuilder() {
+ CelDescriptorPool celDescriptorPool =
+ DefaultDescriptorPool.create(
+ CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(
+ TestAllTypes.getDescriptor().getFile()));
+ DefaultMessageFactory messageFactory = DefaultMessageFactory.create(celDescriptorPool);
+
+ TestAllTypes.Builder builder =
+ (TestAllTypes.Builder)
+ messageFactory.newBuilder("dev.cel.testing.testdata.proto2.TestAllTypes").get();
+
+ assertThat(builder.setSingleInt64(5L).build())
+ .isEqualTo(TestAllTypes.newBuilder().setSingleInt64(5L).build());
+ }
+
+ @Test
+ public void newBuilder_unknownMessage_returnsEmpty() {
+ DefaultMessageFactory messageFactory =
+ DefaultMessageFactory.create(DefaultDescriptorPool.INSTANCE);
+
+ assertThat(messageFactory.newBuilder("unknown_message")).isEmpty();
+ }
+
+ @Test
+ public void newBuilder_unequalDescriptorForSameMessage_returnsDynamicMessage() throws Exception {
+ String fdsContent =
+ Resources.toString(Resources.getResource(Ascii.toLowerCase("value.fds")), UTF_8);
+ FileDescriptorSet fds = TextFormat.parse(fdsContent, FileDescriptorSet.class);
+ ImmutableSet files =
+ CelDescriptorUtil.getFileDescriptorsFromFileDescriptorSet(fds);
+ CelDescriptors celDescriptors = CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(files);
+
+ DefaultMessageFactory messageFactory =
+ DefaultMessageFactory.create(DefaultDescriptorPool.create(celDescriptors));
+
+ assertThat(messageFactory.newBuilder("google.api.expr.Value")).isPresent();
+ assertThat(messageFactory.newBuilder("google.api.expr.Value").get())
+ .isInstanceOf(DynamicMessage.Builder.class);
+ }
+
+ @Test
+ public void getDescriptorPoolTest() {
+ CelDescriptorPool celDescriptorPool =
+ DefaultDescriptorPool.create(
+ CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(
+ TestAllTypes.getDescriptor().getFile()));
+ DefaultMessageFactory messageFactory = DefaultMessageFactory.create(celDescriptorPool);
+
+ assertThat(messageFactory.getDescriptorPool()).isEqualTo(celDescriptorPool);
+ }
+
+ @Test
+ public void combinedMessageFactoryTest() {
+ CombinedMessageFactory messageFactory =
+ new ProtoMessageFactory.CombinedMessageFactory(
+ ImmutableList.of(
+ DefaultMessageFactory.create(DefaultDescriptorPool.INSTANCE),
+ (messageName) ->
+ messageName.equals("test")
+ ? Optional.of(TestAllTypes.newBuilder())
+ : Optional.empty()));
+
+ assertThat(messageFactory.newBuilder("test").get().build())
+ .isEqualTo(TestAllTypes.getDefaultInstance());
+ assertThat(messageFactory.newBuilder("bogus")).isEmpty();
+ }
+}