Skip to content

Commit bb37b1c

Browse files
l46kokcopybara-github
authored andcommitted
Allow evaluation of MessageLite objects in a descriptorless runtime environment
PiperOrigin-RevId: 991999341
1 parent 6a06778 commit bb37b1c

5 files changed

Lines changed: 279 additions & 27 deletions

File tree

‎common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java‎

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,13 @@ private Object readLengthDelimitedField(
140140
case BYTES:
141141
return inputStream.readBytes();
142142
case MESSAGE:
143-
MessageLite.Builder builder =
144-
getDefaultMessageBuilder(fieldDescriptor.getFieldProtoTypeName());
145-
143+
String fieldProtoTypeName = fieldDescriptor.getFieldProtoTypeName();
144+
MessageLiteDescriptor descriptor =
145+
descriptorPool.findDescriptor(fieldProtoTypeName).orElse(null);
146+
if (descriptor == null) {
147+
return RawProtoMessageLiteValue.create(inputStream.readBytes(), fieldProtoTypeName, this);
148+
}
149+
MessageLite.Builder builder = descriptor.newMessageBuilder();
146150
inputStream.readMessage(builder, ExtensionRegistryLite.getEmptyRegistry());
147151
return builder.build();
148152
case STRING:
@@ -152,10 +156,6 @@ private Object readLengthDelimitedField(
152156
}
153157
}
154158

155-
private MessageLite.Builder getDefaultMessageBuilder(String protoTypeName) {
156-
return descriptorPool.getDescriptorOrThrow(protoTypeName).newMessageBuilder();
157-
}
158-
159159
Object getDefaultCelValue(String protoTypeName, String fieldName) {
160160
MessageLiteDescriptor messageDescriptor = descriptorPool.getDescriptorOrThrow(protoTypeName);
161161
return getDefaultCelValue(messageDescriptor.getByFieldNameOrThrow(fieldName));
@@ -200,17 +200,15 @@ private Object decodeWellKnownProto(
200200
}
201201

202202
@Override
203-
@SuppressWarnings("LiteProtoToString") // No alternative identifier to use. Debug only info is OK.
204203
public Object toRuntimeValue(Object value) {
205204
checkNotNull(value);
206205
if (value instanceof MessageLite) {
207206
MessageLite msg = (MessageLite) value;
208207

209-
MessageLiteDescriptor descriptor =
210-
descriptorPool
211-
.findDescriptor(msg)
212-
.orElseThrow(
213-
() -> new NoSuchElementException("Could not find a descriptor for: " + msg));
208+
MessageLiteDescriptor descriptor = descriptorPool.findDescriptor(msg).orElse(null);
209+
if (descriptor == null) {
210+
return RawProtoMessageLiteValue.create(msg.toByteString(), this);
211+
}
214212
WellKnownProto wellKnownProto =
215213
WellKnownProto.getByTypeName(descriptor.getProtoTypeName()).orElse(null);
216214

@@ -279,11 +277,16 @@ private Object getScalarDefaultValue(FieldLiteDescriptor fieldDescriptor) {
279277
case BYTE_STRING:
280278
return CelByteString.EMPTY;
281279
case MESSAGE:
282-
if (WellKnownProto.isWrapperType(fieldDescriptor.getFieldProtoTypeName())) {
280+
String fieldProtoTypeName = fieldDescriptor.getFieldProtoTypeName();
281+
if (WellKnownProto.isWrapperType(fieldProtoTypeName)) {
283282
return NullValue.NULL_VALUE;
284283
}
285-
286-
return getDefaultMessageBuilder(fieldDescriptor.getFieldProtoTypeName()).build();
284+
MessageLiteDescriptor descriptor =
285+
descriptorPool.findDescriptor(fieldProtoTypeName).orElse(null);
286+
if (descriptor == null) {
287+
return RawProtoMessageLiteValue.create(ByteString.EMPTY, fieldProtoTypeName, this);
288+
}
289+
return descriptor.newMessageBuilder().build();
287290
}
288291
throw new IllegalStateException("Unexpected java type: " + type);
289292
}

‎common/src/main/java/dev/cel/common/values/RawProtoMessageLiteValue.java‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public boolean isZeroValue() {
105105
*/
106106
@Override
107107
public Object select(String field) {
108-
throw CelAttributeNotFoundException.forFieldResolution(field);
108+
throw newUnoptimizedFieldResolutionException(field);
109109
}
110110

111111
/**
@@ -114,7 +114,15 @@ public Object select(String field) {
114114
*/
115115
@Override
116116
public Optional<Object> find(String field) {
117-
throw CelAttributeNotFoundException.forFieldResolution(field);
117+
throw newUnoptimizedFieldResolutionException(field);
118+
}
119+
120+
private CelAttributeNotFoundException newUnoptimizedFieldResolutionException(String field) {
121+
return CelAttributeNotFoundException.of(
122+
String.format(
123+
"Error resolving field '%s' on '%s'. Field selection by name is not supported on raw"
124+
+ " proto wire bytes; register its CelLiteDescriptor or enable SelectOptimizer.",
125+
field, celType().name()));
118126
}
119127

120128
@Override
@@ -514,7 +522,10 @@ public static RawProtoMessageLiteValue create(
514522
checkNotNull(protoTypeName);
515523
checkNotNull(protoLiteCelValueConverter);
516524
return new AutoValue_RawProtoMessageLiteValue(
517-
rawWireBytes, StructTypeReference.create(protoTypeName), protoLiteCelValueConverter);
525+
rawWireBytes,
526+
StructTypeReference.create(
527+
protoTypeName.isEmpty() ? UNKNOWN_MESSAGE_TYPE_NAME : protoTypeName),
528+
protoLiteCelValueConverter);
518529
}
519530

520531
RawProtoMessageLiteValue() {}

‎common/src/test/java/dev/cel/common/values/ProtoLiteCelValueConverterTest.java‎

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import dev.cel.common.internal.CelLiteDescriptorPool;
4444
import dev.cel.common.internal.DefaultLiteDescriptorPool;
4545
import dev.cel.common.values.ProtoLiteCelValueConverter.MessageFields;
46+
import dev.cel.expr.conformance.proto3.NestedTestAllTypes;
4647
import dev.cel.expr.conformance.proto3.TestAllTypes;
4748
import dev.cel.expr.conformance.proto3.TestAllTypesCelDescriptor;
4849
import dev.cel.protobuf.CelLiteDescriptor.FieldLiteDescriptor;
@@ -92,6 +93,19 @@ public MessageLiteDescriptor getDescriptorOrThrow(String protoTypeName) {
9293
assertThat(protoMessageLiteValue.value()).isEqualTo(TestAllTypes.getDefaultInstance());
9394
}
9495

96+
@Test
97+
public void fromProtoMessageToCelValue_withoutDescriptor_returnsRawProtoMessageLiteValue() {
98+
ProtoLiteCelValueConverter converterWithoutDescriptors =
99+
ProtoLiteCelValueConverter.newInstance(EMPTY_DESCRIPTOR_POOL);
100+
TestAllTypes msg = TestAllTypes.newBuilder().setSingleInt64(42L).build();
101+
102+
Object adaptedValue = converterWithoutDescriptors.toRuntimeValue(msg);
103+
104+
assertThat(adaptedValue)
105+
.isEqualTo(
106+
RawProtoMessageLiteValue.create(msg.toByteString(), converterWithoutDescriptors));
107+
}
108+
95109
@SuppressWarnings("ImmutableEnumChecker") // Test only
96110
private enum WellKnownProtoTestCase {
97111
BOOL(BoolValue.of(true), true),
@@ -364,17 +378,46 @@ public void getDefaultCelValue_fieldDescriptor_returnsDefault() {
364378
}
365379

366380
@Test
367-
public void getDefaultCelValue_nestedMessageWithoutDescriptor_throwsNoSuchElementException() {
381+
public void getDefaultCelValue_nestedMessageWithoutDescriptor_returnsRawProtoMessageLiteValue() {
368382
FieldLiteDescriptor nestedMsgField =
369383
DESCRIPTOR_POOL
370384
.getDescriptorOrThrow("cel.expr.conformance.proto3.TestAllTypes")
371385
.getByFieldNameOrThrow("single_nested_message");
372386
ProtoLiteCelValueConverter converterWithoutNested =
373387
ProtoLiteCelValueConverter.newInstance(EMPTY_DESCRIPTOR_POOL);
374388

375-
assertThrows(
376-
NoSuchElementException.class,
377-
() -> converterWithoutNested.getDefaultCelValue(nestedMsgField));
389+
Object defaultValue = converterWithoutNested.getDefaultCelValue(nestedMsgField);
390+
391+
assertThat(defaultValue)
392+
.isEqualTo(
393+
RawProtoMessageLiteValue.create(
394+
ByteString.EMPTY,
395+
"cel.expr.conformance.proto3.TestAllTypes.NestedMessage",
396+
converterWithoutNested));
397+
}
398+
399+
@Test
400+
public void readAllFields_nestedMessageWithoutDescriptor_returnsRawProtoMessageLiteValue()
401+
throws Exception {
402+
// DESCRIPTOR_POOL only registers TestAllTypesCelDescriptor, not NestedTestAllTypesCelDescriptor
403+
TestAllTypes msg =
404+
TestAllTypes.newBuilder()
405+
.setOneofType(
406+
NestedTestAllTypes.newBuilder()
407+
.setPayload(TestAllTypes.newBuilder().setSingleInt64(42L)))
408+
.build();
409+
410+
MessageFields fields =
411+
PROTO_LITE_CEL_VALUE_CONVERTER.readAllFields(
412+
msg.toByteArray(), "cel.expr.conformance.proto3.TestAllTypes");
413+
414+
assertThat(fields.values())
415+
.containsExactly(
416+
"oneof_type",
417+
RawProtoMessageLiteValue.create(
418+
msg.getOneofType().toByteString(),
419+
"cel.expr.conformance.proto3.NestedTestAllTypes",
420+
PROTO_LITE_CEL_VALUE_CONVERTER));
378421
}
379422

380423
@Test

‎common/src/test/java/dev/cel/common/values/RawProtoMessageLiteValueTest.java‎

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,28 +71,55 @@ public void create_accessorsAndType() {
7171
}
7272

7373
@Test
74-
public void create_defaultsEmptyTypeName() {
74+
public void create_defaultsUnknownMessageTypeName() {
7575
ByteString bytes = ByteString.copyFromUtf8("test");
76+
7677
RawProtoMessageLiteValue value = RawProtoMessageLiteValue.create(bytes, EMPTY_CONVERTER);
7778

7879
assertThat(value.rawWireBytes()).isEqualTo(bytes);
79-
assertThat(value.celType().name()).isEmpty();
80+
assertThat(value.celType().name()).isEqualTo("cel.@unknownMessage");
81+
}
82+
83+
@Test
84+
public void create_emptyTypeName_normalizesToUnknownMessageTypeName() {
85+
ByteString bytes = ByteString.copyFromUtf8("test");
86+
87+
RawProtoMessageLiteValue value = RawProtoMessageLiteValue.create(bytes, "", EMPTY_CONVERTER);
88+
89+
assertThat(value.rawWireBytes()).isEqualTo(bytes);
90+
assertThat(value.celType().name()).isEqualTo("cel.@unknownMessage");
8091
}
8192

8293
@Test
8394
public void select_throwsCelAttributeNotFoundException() {
8495
RawProtoMessageLiteValue value =
8596
RawProtoMessageLiteValue.create(ByteString.EMPTY, "custom.Message", EMPTY_CONVERTER);
8697

87-
assertThrows(CelAttributeNotFoundException.class, () -> value.select("field"));
98+
CelAttributeNotFoundException e =
99+
assertThrows(CelAttributeNotFoundException.class, () -> value.select("field"));
100+
101+
assertThat(e)
102+
.hasMessageThat()
103+
.isEqualTo(
104+
"Error resolving field 'field' on 'custom.Message'. Field selection by name is not"
105+
+ " supported on raw proto wire bytes; register its CelLiteDescriptor or enable"
106+
+ " SelectOptimizer.");
88107
}
89108

90109
@Test
91110
public void find_throwsCelAttributeNotFoundException() {
92111
RawProtoMessageLiteValue value =
93112
RawProtoMessageLiteValue.create(ByteString.EMPTY, "custom.Message", EMPTY_CONVERTER);
94113

95-
assertThrows(CelAttributeNotFoundException.class, () -> value.find("field"));
114+
CelAttributeNotFoundException e =
115+
assertThrows(CelAttributeNotFoundException.class, () -> value.find("field"));
116+
117+
assertThat(e)
118+
.hasMessageThat()
119+
.isEqualTo(
120+
"Error resolving field 'field' on 'custom.Message'. Field selection by name is not"
121+
+ " supported on raw proto wire bytes; register its CelLiteDescriptor or enable"
122+
+ " SelectOptimizer.");
96123
}
97124

98125
@Test

0 commit comments

Comments
 (0)