Skip to content

Commit 818b259

Browse files
l46kokcopybara-github
authored andcommitted
Separately generate lite descriptors per message
PiperOrigin-RevId: 764355046
1 parent 3bf6818 commit 818b259

10 files changed

Lines changed: 161 additions & 59 deletions

File tree

protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptorGenerator.java

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.common.io.Files;
2121
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
2222
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
23+
import com.google.protobuf.Descriptors.Descriptor;
2324
import com.google.protobuf.Descriptors.FileDescriptor;
2425
import com.google.protobuf.ExtensionRegistry;
2526
import dev.cel.common.CelDescriptorUtil;
@@ -104,54 +105,54 @@ public Integer call() throws Exception {
104105
targetDescriptorProtoPath));
105106
}
106107

107-
GeneratedClass generatedClass = codegenCelLiteDescriptor(targetFileDescriptor);
108-
debugPrinter.print("Generated Class:\n" + generatedClass.code());
109-
110-
generatedClassesBuilder.add(generatedClass);
108+
ImmutableList<GeneratedClass> generatedClasses =
109+
codegenCelLiteDescriptors(targetFileDescriptor);
110+
generatedClassesBuilder.addAll(generatedClasses);
111111
}
112112

113113
JavaFileGenerator.writeSrcJar(outPath, generatedClassesBuilder.build());
114114

115115
return 0;
116116
}
117117

118-
private GeneratedClass codegenCelLiteDescriptor(FileDescriptor targetFileDescriptor)
119-
throws Exception {
118+
private ImmutableList<GeneratedClass> codegenCelLiteDescriptors(
119+
FileDescriptor targetFileDescriptor) throws Exception {
120120
String javaPackageName = ProtoJavaQualifiedNames.getJavaPackageName(targetFileDescriptor);
121121
String javaClassName;
122122

123-
// Derive the java class name. Use first encountered message/enum in the FDS as a default,
124-
// with a suffix applied for uniqueness (we don't want to collide with java protoc default
125-
// generated class name).
126-
if (!targetFileDescriptor.getMessageTypes().isEmpty()) {
127-
javaClassName = targetFileDescriptor.getMessageTypes().get(0).getName();
128-
} else if (!targetFileDescriptor.getEnumTypes().isEmpty()) {
129-
javaClassName = targetFileDescriptor.getEnumTypes().get(0).getName();
130-
} else {
131-
throw new IllegalArgumentException("File descriptor does not contain any messages or enums!");
123+
List<Descriptor> descriptors = targetFileDescriptor.getMessageTypes();
124+
if (descriptors.isEmpty()) {
125+
throw new IllegalArgumentException("File descriptor does not contain any messages!");
132126
}
133127

134-
String javaSuffixName =
135-
overriddenDescriptorClassSuffix.isEmpty()
136-
? DEFAULT_CEL_LITE_DESCRIPTOR_CLASS_SUFFIX
137-
: overriddenDescriptorClassSuffix;
138-
javaClassName += javaSuffixName;
139-
140128
ProtoDescriptorCollector descriptorCollector =
141129
ProtoDescriptorCollector.newInstance(debugPrinter);
142130

143-
debugPrinter.print(
144-
String.format(
145-
"Fully qualified descriptor java class name: %s.%s", javaPackageName, javaClassName));
146-
147-
return JavaFileGenerator.generateClass(
148-
JavaFileGeneratorOption.newBuilder()
149-
.setVersion(version)
150-
.setDescriptorClassName(javaClassName)
151-
.setPackageName(javaPackageName)
152-
.setDescriptorMetadataList(
153-
descriptorCollector.collectCodegenMetadata(targetFileDescriptor))
154-
.build());
131+
ImmutableList.Builder<GeneratedClass> generatedClassBuilder = ImmutableList.builder();
132+
for (Descriptor messageDescriptor : descriptors) {
133+
javaClassName = messageDescriptor.getName();
134+
String javaSuffixName =
135+
overriddenDescriptorClassSuffix.isEmpty()
136+
? DEFAULT_CEL_LITE_DESCRIPTOR_CLASS_SUFFIX
137+
: overriddenDescriptorClassSuffix;
138+
javaClassName += javaSuffixName;
139+
140+
debugPrinter.print(
141+
String.format(
142+
"Fully qualified descriptor java class name: %s.%s", javaPackageName, javaClassName));
143+
144+
generatedClassBuilder.add(
145+
JavaFileGenerator.generateClass(
146+
JavaFileGeneratorOption.newBuilder()
147+
.setVersion(version)
148+
.setDescriptorClassName(javaClassName)
149+
.setPackageName(javaPackageName)
150+
.setDescriptorMetadataList(
151+
descriptorCollector.collectCodegenMetadata(messageDescriptor))
152+
.build()));
153+
}
154+
155+
return generatedClassBuilder.build();
155156
}
156157

157158
private String extractProtoPath(String descriptorPath) {

protobuf/src/main/java/dev/cel/protobuf/ProtoDescriptorCollector.java

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,19 @@ final class ProtoDescriptorCollector {
3737

3838
private final DebugPrinter debugPrinter;
3939

40-
ImmutableList<LiteDescriptorCodegenMetadata> collectCodegenMetadata(
41-
FileDescriptor targetFileDescriptor) {
40+
ImmutableList<LiteDescriptorCodegenMetadata> collectCodegenMetadata(Descriptor descriptor) {
4241
ImmutableList.Builder<LiteDescriptorCodegenMetadata> descriptorListBuilder =
4342
ImmutableList.builder();
4443
ImmutableList<Descriptor> descriptorList =
45-
collect(targetFileDescriptor).stream()
44+
collectNested(descriptor).stream()
4645
// Don't collect WKTs. They are included in the default descriptor pool.
4746
.filter(d -> !WellKnownProto.getByTypeName(d.getFullName()).isPresent())
4847
.collect(toImmutableList());
4948

50-
for (Descriptor descriptor : descriptorList) {
49+
for (Descriptor messageDescriptor : descriptorList) {
5150
LiteDescriptorCodegenMetadata.Builder descriptorCodegenBuilder =
5251
LiteDescriptorCodegenMetadata.newBuilder();
53-
for (Descriptors.FieldDescriptor fieldDescriptor : descriptor.getFields()) {
52+
for (Descriptors.FieldDescriptor fieldDescriptor : messageDescriptor.getFields()) {
5453
FieldLiteDescriptorMetadata.Builder fieldDescriptorCodegenBuilder =
5554
FieldLiteDescriptorMetadata.newBuilder()
5655
.setFieldNumber(fieldDescriptor.getNumber())
@@ -85,16 +84,17 @@ ImmutableList<LiteDescriptorCodegenMetadata> collectCodegenMetadata(
8584
debugPrinter.print(
8685
String.format(
8786
"Collecting message %s, for field %s, type: %s",
88-
descriptor.getFullName(),
87+
messageDescriptor.getFullName(),
8988
fieldDescriptor.getFullName(),
9089
fieldDescriptor.getType()));
9190
}
9291

93-
descriptorCodegenBuilder.setProtoTypeName(descriptor.getFullName());
92+
descriptorCodegenBuilder.setProtoTypeName(messageDescriptor.getFullName());
9493
// Maps are resolved as an actual Java map, and doesn't have a MessageLite.Builder associated.
95-
if (!descriptor.getOptions().getMapEntry()) {
94+
if (!messageDescriptor.getOptions().getMapEntry()) {
9695
String sanitizedJavaClassName =
97-
ProtoJavaQualifiedNames.getFullyQualifiedJavaClassName(descriptor).replace('$', '.');
96+
ProtoJavaQualifiedNames.getFullyQualifiedJavaClassName(messageDescriptor)
97+
.replace('$', '.');
9898
descriptorCodegenBuilder.setJavaClassName(sanitizedJavaClassName);
9999
}
100100

@@ -177,19 +177,17 @@ private static FieldLiteDescriptor.JavaType adaptJavaType(JavaType javaType) {
177177
throw new IllegalArgumentException("Unknown JavaType: " + javaType);
178178
}
179179

180-
private static ImmutableSet<Descriptor> collect(FileDescriptor fileDescriptor) {
180+
private static ImmutableSet<Descriptor> collectNested(Descriptor descriptor) {
181181
ImmutableSet.Builder<Descriptor> builder = ImmutableSet.builder();
182-
for (Descriptor descriptor : fileDescriptor.getMessageTypes()) {
183-
collect(builder, descriptor);
184-
}
185-
182+
collectNested(builder, descriptor);
186183
return builder.build();
187184
}
188185

189-
private static void collect(ImmutableSet.Builder<Descriptor> builder, Descriptor descriptor) {
186+
private static void collectNested(
187+
ImmutableSet.Builder<Descriptor> builder, Descriptor descriptor) {
190188
builder.add(descriptor);
191189
for (Descriptor nested : descriptor.getNestedTypes()) {
192-
collect(builder, nested);
190+
collectNested(builder, nested);
193191
}
194192
}
195193

protobuf/src/test/java/dev/cel/protobuf/CelLiteDescriptorTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ public void getProtoTypeNamesToDescriptors_containsAllMessages() {
4040
assertThat(protoNamesToDescriptors).containsKey("cel.expr.conformance.proto3.TestAllTypes");
4141
assertThat(protoNamesToDescriptors)
4242
.containsKey("cel.expr.conformance.proto3.TestAllTypes.NestedMessage");
43-
assertThat(protoNamesToDescriptors)
44-
.containsKey("cel.expr.conformance.proto3.NestedTestAllTypes");
4543
}
4644

4745
@Test

protobuf/src/test/java/dev/cel/protobuf/ProtoDescriptorCollectorTest.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.common.collect.ImmutableList;
2020
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
21+
import dev.cel.expr.conformance.proto3.NestedTestAllTypes;
2122
import dev.cel.expr.conformance.proto3.TestAllTypes;
2223
import dev.cel.testing.testdata.MultiFile;
2324
import org.junit.Test;
@@ -31,11 +32,14 @@ public void collectCodegenMetadata_containsAllDescriptors() {
3132
ProtoDescriptorCollector collector =
3233
ProtoDescriptorCollector.newInstance(DebugPrinter.newInstance(false));
3334

34-
ImmutableList<LiteDescriptorCodegenMetadata> descriptors =
35-
collector.collectCodegenMetadata(TestAllTypes.getDescriptor().getFile());
35+
ImmutableList<LiteDescriptorCodegenMetadata> testAllTypesDescriptors =
36+
collector.collectCodegenMetadata(TestAllTypes.getDescriptor());
37+
ImmutableList<LiteDescriptorCodegenMetadata> nestedTestAllTypesDescriptors =
38+
collector.collectCodegenMetadata(NestedTestAllTypes.getDescriptor());
3639

3740
// All proto messages, including transitive ones + maps
38-
assertThat(descriptors).hasSize(166);
41+
assertThat(testAllTypesDescriptors).hasSize(165);
42+
assertThat(nestedTestAllTypesDescriptors).hasSize(1);
3943
}
4044

4145
@Test
@@ -44,7 +48,7 @@ public void collectCodegenMetadata_withProtoDependencies_containsAllDescriptors(
4448
ProtoDescriptorCollector.newInstance(DebugPrinter.newInstance(false));
4549

4650
ImmutableList<LiteDescriptorCodegenMetadata> descriptors =
47-
collector.collectCodegenMetadata(MultiFile.getDescriptor().getFile());
51+
collector.collectCodegenMetadata(MultiFile.getDescriptor());
4852

4953
assertThat(descriptors).hasSize(3);
5054
assertThat(
@@ -60,7 +64,7 @@ public void collectCodegenMetadata_withProtoDependencies_doesNotContainImportedP
6064
ProtoDescriptorCollector.newInstance(DebugPrinter.newInstance(false));
6165

6266
ImmutableList<LiteDescriptorCodegenMetadata> descriptors =
63-
collector.collectCodegenMetadata(MultiFile.getDescriptor().getFile());
67+
collector.collectCodegenMetadata(MultiFile.getDescriptor());
6468

6569
assertThat(
6670
descriptors.stream()

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ java_library(
243243
"//runtime:type_resolver",
244244
"//runtime:unknown_attributes",
245245
"//runtime:unknown_options",
246+
"//testing/protos:message_with_enum_cel_java_proto",
247+
"//testing/protos:message_with_enum_java_proto",
246248
"//testing/protos:multi_file_cel_java_proto",
247249
"//testing/protos:multi_file_java_proto",
248250
"//testing/protos:single_file_java_proto",

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import dev.cel.common.values.CelValueProvider;
4545
import dev.cel.common.values.ProtoMessageLiteValueProvider;
4646
import dev.cel.expr.conformance.proto3.NestedTestAllTypes;
47+
import dev.cel.expr.conformance.proto3.NestedTestAllTypesCelLiteDescriptor;
4748
import dev.cel.expr.conformance.proto3.TestAllTypes;
4849
import dev.cel.expr.conformance.proto3.TestAllTypesCelLiteDescriptor;
4950
import dev.cel.extensions.CelLiteExtensions;
@@ -320,7 +321,10 @@ public void eval_protoMessage_primitiveWithDefaults(String checkedExpr) throws E
320321
.setValueProvider(
321322
ProtoMessageLiteValueProvider.newInstance(
322323
dev.cel.expr.conformance.proto2.TestAllTypesCelLiteDescriptor.getDescriptor(),
323-
TestAllTypesCelLiteDescriptor.getDescriptor()))
324+
dev.cel.expr.conformance.proto2.NestedTestAllTypesCelLiteDescriptor
325+
.getDescriptor(),
326+
TestAllTypesCelLiteDescriptor.getDescriptor(),
327+
NestedTestAllTypesCelLiteDescriptor.getDescriptor()))
324328
.build();
325329
// Ensures that all branches of the OR conditions are evaluated, and that appropriate defaults
326330
// are returned for primitives.
@@ -454,7 +458,10 @@ public void eval_protoMessage_safeTraversal(String checkedExpr) throws Exception
454458
.setValueProvider(
455459
ProtoMessageLiteValueProvider.newInstance(
456460
dev.cel.expr.conformance.proto2.TestAllTypesCelLiteDescriptor.getDescriptor(),
457-
TestAllTypesCelLiteDescriptor.getDescriptor()))
461+
dev.cel.expr.conformance.proto2.NestedTestAllTypesCelLiteDescriptor
462+
.getDescriptor(),
463+
TestAllTypesCelLiteDescriptor.getDescriptor(),
464+
NestedTestAllTypesCelLiteDescriptor.getDescriptor()))
458465
.build();
459466
// Expr: proto2.oneof_type.payload.repeated_string
460467
CelAbstractSyntaxTree ast = readCheckedExpr(checkedExpr);
@@ -483,7 +490,10 @@ public void eval_protoMessage_deepTraversalReturnsRepeatedStrings(String checked
483490
.setValueProvider(
484491
ProtoMessageLiteValueProvider.newInstance(
485492
dev.cel.expr.conformance.proto2.TestAllTypesCelLiteDescriptor.getDescriptor(),
486-
TestAllTypesCelLiteDescriptor.getDescriptor()))
493+
dev.cel.expr.conformance.proto2.NestedTestAllTypesCelLiteDescriptor
494+
.getDescriptor(),
495+
TestAllTypesCelLiteDescriptor.getDescriptor(),
496+
NestedTestAllTypesCelLiteDescriptor.getDescriptor()))
487497
.build();
488498
// Expr: proto2.oneof_type.payload.repeated_string
489499
CelAbstractSyntaxTree ast = readCheckedExpr(checkedExpr);

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,11 @@
5454
import dev.cel.expr.conformance.proto3.TestAllTypes.NestedMessage;
5555
import dev.cel.expr.conformance.proto3.TestAllTypesCelDescriptor;
5656
import dev.cel.parser.CelStandardMacro;
57+
import dev.cel.testing.testdata.MessageWithEnum;
58+
import dev.cel.testing.testdata.MessageWithEnumCelDescriptor;
5759
import dev.cel.testing.testdata.MultiFile;
5860
import dev.cel.testing.testdata.MultiFileCelDescriptor;
61+
import dev.cel.testing.testdata.SimpleEnum;
5962
import dev.cel.testing.testdata.SingleFileCelDescriptor;
6063
import dev.cel.testing.testdata.SingleFileProto.SingleFile;
6164
import java.util.ArrayList;
@@ -660,4 +663,32 @@ public void eval_dynFunctionReturnsProto() throws Exception {
660663

661664
assertThat(result).isEqualToDefaultInstance();
662665
}
666+
667+
@Test
668+
public void eval_withEnumField() throws Exception {
669+
CelCompiler celCompiler =
670+
CelCompilerFactory.standardCelCompilerBuilder()
671+
.addVar(
672+
"msg", StructTypeReference.create(MessageWithEnum.getDescriptor().getFullName()))
673+
.addMessageTypes(MessageWithEnum.getDescriptor())
674+
.build();
675+
CelLiteRuntime celLiteRuntime =
676+
CelLiteRuntimeFactory.newLiteRuntimeBuilder()
677+
.setStandardFunctions(CelStandardFunctions.newBuilder().build())
678+
.setValueProvider(
679+
ProtoMessageLiteValueProvider.newInstance(
680+
MessageWithEnumCelDescriptor.getDescriptor()))
681+
.build();
682+
CelAbstractSyntaxTree ast = celCompiler.compile("msg.simple_enum").getAst();
683+
684+
Long result =
685+
(Long)
686+
celLiteRuntime
687+
.createProgram(ast)
688+
.eval(
689+
ImmutableMap.of(
690+
"msg", MessageWithEnum.newBuilder().setSimpleEnum(SimpleEnum.BAR)));
691+
692+
assertThat(result).isEqualTo(SimpleEnum.BAR.getNumber());
693+
}
663694
}

testing/protos/BUILD.bazel

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ alias(
1919
actual = "//testing/src/test/resources/protos:multi_file_cel_java_proto",
2020
)
2121

22+
alias(
23+
name = "message_with_enum_java_proto",
24+
actual = "//testing/src/test/resources/protos:message_with_enum_java_proto",
25+
)
26+
2227
alias(
2328
name = "multi_file_cel_java_proto_lite",
2429
actual = "//testing/src/test/resources/protos:multi_file_cel_java_proto_lite",
@@ -43,3 +48,8 @@ alias(
4348
name = "test_all_types_cel_java_proto3",
4449
actual = "//testing/src/test/resources/protos:test_all_types_cel_java_proto3",
4550
)
51+
52+
alias(
53+
name = "message_with_enum_cel_java_proto",
54+
actual = "//testing/src/test/resources/protos:message_with_enum_cel_java_proto",
55+
)

testing/src/test/resources/protos/BUILD.bazel

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ proto_library(
3131
deps = [":single_file_proto"],
3232
)
3333

34+
proto_library(
35+
name = "message_with_enum_proto",
36+
srcs = ["message_with_enum.proto"],
37+
)
38+
39+
java_proto_library(
40+
name = "message_with_enum_java_proto",
41+
deps = [":message_with_enum_proto"],
42+
)
43+
3444
# Test only. java_proto_library supports generating a jar with multiple proto deps,
3545
# so we must test this case as well for lite descriptors.
3646
# buildifier: disable=LANG_proto_library-single-deps
@@ -86,3 +96,10 @@ java_lite_proto_cel_library_impl(
8696
java_proto_library_dep = "@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto",
8797
deps = ["@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_proto"],
8898
)
99+
100+
java_lite_proto_cel_library_impl(
101+
name = "message_with_enum_cel_java_proto",
102+
java_descriptor_class_suffix = "CelDescriptor",
103+
java_proto_library_dep = ":message_with_enum_java_proto",
104+
deps = [":message_with_enum_proto"],
105+
)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
syntax = "proto3";
16+
17+
package dev.cel.testing.testdata;
18+
19+
option java_multiple_files = true;
20+
option java_package = "dev.cel.testing.testdata";
21+
option java_outer_classname = "MessageWithEnumProto";
22+
23+
message MessageWithEnum {
24+
SimpleEnum simple_enum = 1;
25+
}
26+
27+
enum SimpleEnum {
28+
FOO = 0;
29+
BAR = 1;
30+
BAZ = 2;
31+
}

0 commit comments

Comments
 (0)